Introduction

In one of the greatest cities in the world, NYC, we have an abundant amount of options to choose from when it comes to where we eat, whether we are looking for fine dining, fast food, or something in between. The cuisine choices seem just as endless. Is there a good way to help choose a restaurant to eat in or a general location? Although not an easy task, one thing we can all agree on is that nobody wants to eat in a restaurant ridden with health violations. In our project, we will explore and analyze NYC’s restaurant inspection results from 2013-2017 of the five boroughs.

We are interested in answering:

  1. Which cuisines have the least and most violations along with their associated score.

  2. What parts of NYC have the least and most violations?

  3. We hypothesize that location is highly associated with inspection grade and so we will be searching for patterns between these variables.

  4. We are also interested in seeing how inspection grade and score changes over time (years) based upon cuisine and location.

  5. A description of violations is also provided in the data and we would like to better understand the common causes of those violations based upon the description.

  6. Is there a pattern/trend in violations/inspection score/grade based upon restaurant chains?

We found this data set by exploring NYC open data sets. Our love for food and health made this a great option. One of our team members who recently moved to NYC is highly selective about the food she eats due to her rare health condition and hence, having the kinds of questions mentioned above answered can prove to be a great asset to both her and others suffering from health conditions or who may just be picky eaters! The data can be found and downloaded here: https://data.cityofnewyork.us/Health/DOHMH-New-York-City-Restaurant-Inspection-Results/xx67-kt59

To download, click the export button on the right -> Download as -> choose your format. For this assignement we used the CSV format.

Team

Team Members:
  • Jonathan Galsurkar
  • Lakshya Garg
Task Distribution

Get table format for this

Analysis of Data Quality

To begin our analysis of data quality, let’s load our data.

ViolationsData <- read.csv(file="inspection.csv", header=TRUE, sep=",", as.is=TRUE)

In order to more easily work with our data let’s ensure that every date is in a date format rather than a string and every string is a factor variable.

library(dplyr)
library(tidyr)
##Date conversion
ViolationsData <- ViolationsData %>%
  mutate(INSPECTION.DATE= as.Date(INSPECTION.DATE, format= "%m/%d/%Y"))%>%
  mutate(GRADE.DATE= as.Date(GRADE.DATE, format= "%m/%d/%Y"))%>%
  mutate(RECORD.DATE= as.Date(RECORD.DATE, format= "%m/%d/%Y"))
##Factor conversion
ViolationsData <- ViolationsData %>%
  mutate(CUISINE.DESCRIPTION= as.factor(CUISINE.DESCRIPTION))%>%
  mutate(BORO= as.factor(BORO))%>%
  mutate(VIOLATION.CODE= as.factor(VIOLATION.CODE))%>%
  mutate(CRITICAL.FLAG= as.factor(CRITICAL.FLAG))%>%
  mutate(GRADE= as.factor(GRADE))%>%
  mutate(INSPECTION.TYPE= as.factor(INSPECTION.TYPE))

Many of the questions we are interested in answering involve trends across restuarant locations. We first checked to see the number of restuarant inspections by borough. From the plot below, we noticed that there were a number of inspections in which the Borough information was missing and won’t help our analysis.

library(ggplot2)
boroughPlot <- ggplot(ViolationsData, aes(BORO, fill=BORO))
boroughPlot + geom_bar()+ theme(legend.position="none") +
  ggtitle('Inspection Count by Borough') + 
  labs(x = "Borough", y ="Number of Inspections")

Another main feature of our data set is the inspection year since we wish to explore patters in inspection grades/scores over the years. From the plot below, we noticed that there is almost no inspection data before 2013 and surprisingly more inspection data in 1900 than 2012 and 2011. We decided to only work with data from 2013 and up. It is important to note that the number of inspections for 2017 is low since the 2017 data is only available from January - March.

violationYearsPlot <- ggplot(ViolationsData, aes(factor(as.numeric(format(INSPECTION.DATE, '%Y'))), fill="Red"))
violationYearsPlot + geom_bar()+coord_flip()+ theme(legend.position="none") +xlab("Inspection Year")+ylab("Inspection Counts") + ggtitle("Inspection Count by Year")

Another crucial feature of our data set is grade a restaurant received after inspection. We decided to plot a stacked bar chart for to see the count of each type of grade for every cuisine. We used a stacked bar chart because we wanted to quickly assess the magnitude missing without taking up extra room. From the plot below, it was shocking to see that we generally had more missing grades than grades. This was true regardlss of cuisine. It was also interested that the grades were purely missing and not categorized as “Not Yet Graded”.

ggplot(ViolationsData, aes(CUISINE.DESCRIPTION, fill = GRADE)) + geom_bar() + 
  coord_flip()+ggtitle("stacked bar chart of grade distributions across cuisines")

To stay on the topic of grades, after researching the letter grading program, we found the following information: + A score of less than 14 points on either initial or re-inspection results in an “A” grade + On re-inspection, a score of 14-27 points means a restaurant receives both a “B” grade and a “Grade Pending” card. + On re-inspection, a score of 28 or more points means a restaurant receives both a “C” grade and a “Grade Pending” card.

Both Z and P represent grade pending, however P represents a Grade Pending issued on re-opening following an initial inspection that resulted in a closure.

We also discovered that not every inspection is “gradable”. Gradable inspections have the following properties:

This can probably explain a fair amount of the missing grade data observed in our plot.

According to the ABOUT the data set page: The SCORE and GRADE fields may be inconsistent with each other because of limitations or errors in the data systems. That is to say, scores of 0-13, 14-27 and 28+ are not always accompanied by A, B and C grades, respectively, when they should be. There may also be cases where a grade card was given out but a record of that grade issuance is missing from the data system, and therefore missing from this dataset, even though the SCORE field is populated. Note that when initial inspections are adjudicated down to the A range, the absence of an accompanying grade associated with that inspection is correct, because the grade would not be assigned until the re-inspection is performed.

To gain some final insight on the data quality, we decided to plot the relationshp between the number of missing scores by grade and by whether or not a violation was reported. We transformed the actions taken into three categories: 1. No violations were recorded at the time of this inspection to No Violation 2. Any action reported to Violation reported 3. Missing actions to NA We then counted if the score was provided or not.

library(dplyr)
library(tidyr)
score_grade <- ViolationsData[ -c(1:9, 11:13, 16:18) ]
score_grade[score_grade == ''] <- NA
score_grade_combos <- score_grade  %>% mutate(missing_score = ifelse(is.na(SCORE), "yes", "no"))
score_grade_missing <- count(score_grade_combos, c('GRADE', 'missing_score', 'ACTION'))
score_grade_missing <- score_grade_missing %>% 
  mutate(violation = ifelse(ACTION == 'No violations were recorded at the time of this inspection.', 
                                          "No violation", 
                            ifelse(is.na(score_grade_missing$ACTION), NA, "Violation Reported"))) %>%
  select(-c(ACTION))
ggplot(score_grade_missing, aes(x = GRADE, y = log(freq), fill = missing_score)) + 
  geom_bar(stat = 'identity', position = 'dodge') + facet_wrap(~violation) + 
  ggtitle("Grade and missing score combinations by violation report") +
  labs(x = "Grade", y = "Log(Frequency)")

Interesting insights from plot above are:

  1. Grades of NA reported high frequency of score in both No violation and violation reported category.

  2. At first, it appears that high scores are related to low grades or needs grading but then we find restaurants with a grade of A that has the same score as a restaurant with a grade of C.

  3. Another insight by just looking at the data is we surprisingly saw that restaurants with a critical flag still receive grades of A.This needs further analysis. SHOULD WE MAKE A PLOT FOR THIS????

Executive Summary

NYC puts a lot of time and money into inspecting restuarants. Why would they go through all this trouble? To ensure quality meals and satisfaction of NYC residents of course! It is important for restaurants and establishment that meet at least the minimum requirements of health and safety regulations in order to promote less food sick residents and a cleaner NYC.

How can we tell if these inspections are actually working to promote restuarants to meet regulations? Let’s take a look at the proportion of scores that were graded an A in 2013 and the proportion of scores that were graded an A in 2016. Let’s view this information by location so that you know what neighborhood to choose when you’re craving a restaurant.

The darker the shade of red, the higher proportion of A grades. It is clear that over the last 3 years, the proportion of restuarants with a grade of an A has inscreased. NYC inspections must be working to improve the quality of the restuarants we eat in! In 2013, it seems that the there were only a few neighborhoods with an extremely high proportion of A grades. Only 3 areas in Staten Island, 1 in Long Island, a few in Brooklyn, Manhattan, and the Bronx. In 2016 on the other hand, the proportion of A grades is very high almost regardles of neighborhood. Keep it up NYC restaruant inspections!

TO see this from another angle, let’s look at the average scores per neighborhood in 2013 and 2016. THEY WERE BINNED THIS WAYJFDHJKHFLSDFFSD

Just look how much lighter the color of each neighborhood got! Lighter colors mean a lower average score, which correlates to less violations and a healthier establishment!

Well now we know what neighborhoods to be in for establishments with low scores and A grades, but what are the average scores based upon cuisine?

It is interesting that Not Listed/Not Applicable and Other made it to the top 10 but there you have it, the best and worst cuisines based upon their average scores.

Main Analysis

After analyzing the quality of the data set, we got rid of data in which the Borough is missing and the year is before 2013.

ViolationsData <- ViolationsData %>% filter (BORO != "Missing")
ViolationsData <- ViolationsData %>% filter(
  as.numeric(format(INSPECTION.DATE , '%Y')) > 2012)

Now we can get a better picture of the total number of inspections by year and borough.

boroughPlot <- ggplot(ViolationsData, aes(BORO,fill=BORO))
boroughPlot + geom_bar() + theme(legend.position="none") + 
  facet_wrap(~factor(as.numeric( format(INSPECTION.DATE , '%Y')))) + 
  ggtitle("Total Inspections from 2013-2017 in each Borough") +
  xlab("Borough") + ylab("Number of Inspections") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

After 2013, there seems to be a consistent amount of inspections acros the years. The number of inspections by borough also seems to make sense since we expect Manhattan to have the largest number of restuarants.

Next, we took a look at the grade distribution by borough.

inspection_grades <- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, -ZIPCODE, -PHONE, -ACTION, -VIOLATION.CODE, -VIOLATION.DESCRIPTION, -CRITICAL.FLAG, -SCORE, -GRADE.DATE, -RECORD.DATE, -INSPECTION.TYPE,-CUISINE.DESCRIPTION)
inspection_grades_woyear<-inspection_grades %>% select(-INSPECTION.DATE)
inspection_grades_woyear <- inspection_grades_woyear %>% gather(key, value, -BORO) %>% group_by(BORO, key, value) %>% tally %>% spread(value, n, fill = 0)# %>% gather(blah, -BORO, -key) #summarize(aprop = A/(A+B+C+))
names(inspection_grades_woyear)[names(inspection_grades_woyear)=="key"] <- "grade"
names(inspection_grades_woyear)[names(inspection_grades_woyear)==""] <- "unknown"
inspection_grades_woyear <- inspection_grades_woyear %>% gather(key, value, -BORO,-grade)
RelFreq<-function(m){
   ((m )/sum(m))
 }
inspection_grades_woyear<-inspection_grades_woyear %>% group_by(BORO,grade) %>%
    mutate(percentage = (value/sum(value))*100)
ggplot(inspection_grades_woyear, aes(BORO, percentage ,fill= key)) + 
  geom_bar(stat="identity", position = "dodge") +
  ggtitle('%Grade distribution across boroughs') + 
  labs(x = "boroughs", y ="percentage")+guides(fill=guide_legend(title="Grade"))

We initially hypothesized that we would see a pattern in grade distribution by borough, however, the plot shows us that grade distributions are almost the same regardless of borough.

inspection_grades_year<-inspection_grades%>% mutate(year=factor(as.numeric(format(INSPECTION.DATE , '%Y'))))
inspection_grades_year<-inspection_grades_year%>% select(-INSPECTION.DATE)
 inspection_grades_year <- inspection_grades_year %>% gather(key, value, -BORO,-year) %>% group_by(BORO,year, key, value) %>% 
   tally %>% spread(value, n, fill = 0)# %>% gather(blah, -BORO, -key) #summarize(aprop = A/(A+B+C+))
names(inspection_grades_year)[names(inspection_grades_year)=="key"] <- "grade"
names(inspection_grades_year)[names(inspection_grades_year)==""] <- "unknown"
inspection_grades_year <- inspection_grades_year %>% gather(key, value, -BORO,-grade,-year)
RelFreq<-function(m){
   ((m )/sum(m))
 }
inspection_grades_year<-inspection_grades_year %>% group_by(BORO,grade,year) %>%
    mutate(percentage = (value/sum(value))*100)
ggplot(inspection_grades_year, aes(BORO, percentage ,fill= key)) + 
  geom_bar(stat="identity", position = "dodge") +facet_wrap(~year,nrow=2,ncol=3)+
  guides(fill=guide_legend(title="Grade"))+
  ggtitle("%Grade distribution by BOROUGH")

Adding year to this analysis showed us an increase in the proportian of As in Sataten Island in 2015 but seemed consistent throughout the rest of the plot.

We decided that borough may be too general and thus looked at grade distribution by zip code across the various years. We used a heat map to do so.

library(viridis)
nonYearDataForHeatMap<-ViolationsData %>% select(GRADE,ZIPCODE)
#nonYearDataForHeatMap<-nonYearDataForHeatMap%>%mutate(gradeYear=as.numeric(format(GRADE.DATE , '%Y')))
nonYearDataForHeatMap <- nonYearDataForHeatMap %>% group_by(GRADE,ZIPCODE) %>% tally 
RelFreq<-function(m){
   ((m )/sum(m))
}
nonYearDataForHeatMap<-  nonYearDataForHeatMap %>% group_by(ZIPCODE)%>%
    mutate(percentage = (n/sum(n))*100)
ggplot(nonYearDataForHeatMap, aes(GRADE, 
                        ZIPCODE, fill = percentage)) +
  geom_tile() +
  scale_fill_viridis() +
  #facet_wrap(~BORO)
  ggtitle("Percentage grade distribution by zipcode across years\n ")+
  theme(axis.text.x=element_text(angle=-45, hjust=.1),
        plot.title=element_text(margin=margin(b=.5), size = 15))+ylab("Grade")

DISCUSS WITH LAKSHYA

The next part of our analysis was to look at the average scores of each cuisine. With this information, we can help consumers see what kinds of establishments have the best and worst scores on average. This in term can help a consumer choose a type of cuisine when they are hungry.

average_scores <- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, -ZIPCODE, 
                                            -PHONE, -INSPECTION.DATE, -ACTION, 
                                            -VIOLATION.CODE, -VIOLATION.DESCRIPTION, 
                                            -CRITICAL.FLAG, -GRADE, -GRADE.DATE, 
                                            -RECORD.DATE, -INSPECTION.TYPE)
average_cuisine <- average_scores %>%  group_by(CUISINE.DESCRIPTION) %>% na.omit %>%
  summarize(average_score = mean(SCORE))
average_cuisine <- arrange(average_cuisine, -average_score)
ggplot(average_cuisine, aes(reorder(x = CUISINE.DESCRIPTION, --average_score), average_score, fill = CUISINE.DESCRIPTION)) + geom_bar(stat='identity') + coord_flip() + theme(legend.position = 'none')+ggtitle("Average score across cuisines")+xlab("Cuisine description")+ylab("average score")+geom_text(aes(label = sprintf("%.2f", average_score)),position=position_stack(vjust=0.5),
                                                                                                                     vjust = 0.5, size = 3)

worst10 <- average_cuisine[1:10,]
ggplot(worst10, aes(reorder(x = CUISINE.DESCRIPTION, --average_score), average_score, fill = CUISINE.DESCRIPTION)) + geom_bar(stat='identity') + coord_flip() + theme(legend.position = 'none')+ggtitle("Average score of Worst 10 cuisines")+xlab("Cuisine description")+ylab("average score")+geom_text(aes(label = sprintf("%.2f", average_score)),position=position_stack(vjust=0.5))

n <- nrow(average_cuisine)
best10 <- average_cuisine[(n- 10):n,]
best10 <- best10[order(best10$average_score),]
ggplot(best10, aes(reorder(x = CUISINE.DESCRIPTION, -average_score), average_score, fill = CUISINE.DESCRIPTION)) + geom_bar(stat='identity') + coord_flip() + theme(legend.position = 'none')+ggtitle("Average score of top 10 cuisines")+xlab("Cuisine description")+ylab("average score")+geom_text(aes(label = sprintf("%.2f", average_score)),position = position_dodge(width = 1),#position=position_stack(vjust=0.5)
                                                                                                                     vjust = 0.5, size = 4)

It is interesting that the mysterious “not applicable”" cuisine is one of the best!

In this next plot, we wanted to see if the average score significantly differed by Borough

average_borough <- average_scores %>%  group_by(BORO) %>% na.omit %>%
  summarize(average_score = mean(SCORE))
ggplot(average_borough, aes(reorder(x = BORO, --average_score), average_score, fill = BORO)) + geom_bar(stat='identity') + theme(legend.position = 'none')+ggtitle("Average score across boroughs")+xlab("Borough")+ylab("average score")+geom_text(aes(label = sprintf("%.2f", average_score)),position = position_dodge(width = 1),
                                                                                                                     vjust = -0.5, size = 3)

Although we see slight difference, the boroughs have approximately the same average score. Staten Island may be the one exception. We also faceted by year to see if that made a difference, but the results were essentially the same as in the plot above.

So far we looked at grades and scores but not the actual violations. Whare are the top violations restaurants usually face?

violations<- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, -ZIPCODE, 
                                            -PHONE, -INSPECTION.DATE, -ACTION, 
                                            -VIOLATION.CODE, 
                                            -CRITICAL.FLAG, -GRADE, -GRADE.DATE, 
                                            -RECORD.DATE, -INSPECTION.TYPE)
vs <- violations %>%  group_by(VIOLATION.DESCRIPTION) %>% na.omit %>%
  summarize(count = n())
vs <- arrange(vs, -count)
topviolations <- vs[1:10,]
library(stringr)
topviolations$viol = str_wrap(topviolations$VIOLATION.DESCRIPTION, width = 15)
ggplot(topviolations, aes(reorder(viol, -count), count,fill=viol)) + geom_bar(stat='identity') + 
  theme(legend.position = 'none')+ggtitle("Top 10 violations")+xlab("Violation description")+ylab("count")+geom_text(aes(label = sprintf("%.0f", count)),
                                                                                                                  position = position_dodge(width = 1),
                                                                                                                     vjust = -0.5, size = 7)

We also hypothesized that the type of inspections would generally vary by Borough but even this was mostly consistent.

 library(viridis)
mos<- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, -ZIPCODE, 
                                           -PHONE, -ACTION, 
                                           -VIOLATION.CODE, 
                                           -CRITICAL.FLAG, -GRADE, -GRADE.DATE, 
                                           -RECORD.DATE, -SCORE, -CUISINE.DESCRIPTION, -VIOLATION.DESCRIPTION)
mos$year <- factor(as.numeric( format(mos$INSPECTION.DATE , '%Y')))
mos <- mos %>% select(-INSPECTION.DATE)
average_mos <- mos %>% group_by(BORO, year, INSPECTION.TYPE) %>% tally %>%
 group_by(BORO, year)  %>% 
 mutate(p = n / sum(n))
#average_cuisine <- arrange(average_cuisine, -average_score)
library(vcd)
average_mos <- average_mos %>% select(-n)
ggplot(average_mos, aes(BORO, 
                       INSPECTION.TYPE, fill = p)) +
 geom_tile() +
 scale_fill_viridis() +
 #facet_wrap(~BORO)
 ggtitle("AVERAGE VIOLATION SCORE BY ZIP CODE\n ")+
 theme(axis.text.x=element_text(angle=-45, hjust=.1),
       plot.title=element_text(margin=margin(b=.5), size = 15))+guides(fill=guide_legend(title="percentage"))

One thing we noticed was that Staten island has the most Cycle Inspection/Initial Inspection. Maybe they were opening up many new restuarants.

One factor of our data set we haven’t discussed too much is the Critical flag, saying whether or not a violation is critical. Once again, we assumed that over the years and across different boroughs, there would be some pattern in critical violations. As can be inferred from the following plot, there is not.

CriticalityData<- ViolationsData %>% select(BORO,INSPECTION.DATE,CRITICAL.FLAG) %>% na.omit()
CriticalityData$year<-factor(as.numeric( format(CriticalityData$INSPECTION.DATE , '%Y')))
#CriticalityData <- CriticalityData %>%select(-INSPECTION.DATE)
CriticalityData <- CriticalityData %>%select(-INSPECTION.DATE) %>%gather(key, value, -BORO, -year) %>% group_by(BORO, year,key,value) %>%tally %>% spread(value, n, fill = 0)
names(CriticalityData)[5] <- "NotApplicable"
names(CriticalityData)[6] <- "NonCritical"
CriticalityDataPercentage<-CriticalityData %>% summarize(criticalPercent = Critical/(Critical+NotApplicable+NonCritical), NotApplicablePercent = NotApplicable/(Critical+NotApplicable+NonCritical), NonCriticalPercent = NonCritical/(Critical+NotApplicable+NonCritical))#
names(CriticalityDataPercentage)[4] <- "Critical"
names(CriticalityDataPercentage)[5] <- "NotApplicable"
names(CriticalityDataPercentage)[6] <- "NonCritical"
CriticalityDataPercentage<-CriticalityDataPercentage%>% gather(key1, value, -BORO, -year,-key)
ggplot(CriticalityDataPercentage, aes(BORO, value, fill = key1)) + 
  geom_bar(stat="identity", position = "dodge") + 
  scale_fill_brewer(palette = "Set1")+ xlab("BOROUGH")+ylab("CRITICALITY PERCENTAGES") +theme(axis.text.x = element_text(angle = 45, hjust = 1))+ ggtitle("Grouped barchart of grade frequency in each cuisine category \n when there was a violation reported")+facet_wrap(~year,nrow=2,ncol=3)+guides(fill=guide_legend(title="Criticality \n category"))

We attempted to further analyze the critical flag but we learned that the critical flag did not have too much impact on grade or scores. There was a lot of data with a grade of A but a Critical flag while others with a very high score by no critical flag.

For the remainder of the analysis, we will be working with Tableau. We chose Tableau because we wanted to plot elegent maps. Tableau provides a simpler and more elegent way to do so than in R.

The code chunks will generate the data that we upload to Tableau. In the following analysis, we explore the proportion of A grades based on neighborhood over the years.

#####GENERATE GRADE PROPOERTION DATASET BY YEAR FOR TABLEAU PLOTTING
zipdata<- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, 
                                            -PHONE, -ACTION, 
                                            -VIOLATION.CODE, 
                                            -CRITICAL.FLAG, -SCORE, -GRADE.DATE, 
                                            -RECORD.DATE, -INSPECTION.TYPE, -VIOLATION.DESCRIPTION, -CUISINE.DESCRIPTION, -BORO) %>% na.omit()
zipdata$year<-factor(as.numeric( format(zipdata$INSPECTION.DATE , '%Y')))
zipdataGradePercentage <- zipdata %>%select(-INSPECTION.DATE) %>%gather(key, value, -ZIPCODE, -year) %>% group_by(ZIPCODE, year, key, value) %>%
  tally %>% spread(value, n, fill = 0) %>% summarize(aprop = A/(A+B+C), bprop = B/(A+B+C), cprop = C/(A+B+C) )

# ggplot(zipdataGradePercentage, aes(reorder(x = ZIPCODE, --aprop), aprop)) + geom_bar(stat='identity') + 
#   coord_flip() + facet_wrap(~year)
#   theme( axis.text = element_text(size = 3))
knitr::include_graphics('Aprop1.png')

knitr::include_graphics('Aprop2.png')

knitr::include_graphics('Aprop3.png')

knitr::include_graphics('Aprop4.png')

knitr::include_graphics('Aprop5.png')

Over the years, the proportion of A’s has increased, as represented by the darker red shading of the map. It is too early to study 2017 in depth due to the limited number of inspections so far.

Let’s look at how the proportion of B’s change.

knitr::include_graphics('Bprop1.png')

knitr::include_graphics('Bprop2.png')

knitr::include_graphics('Bprop3.png')

knitr::include_graphics('Bprop4.png')

knitr::include_graphics('Bprop5.png')

We can see that over the years, there seem to be less B’s which makes sense since over the years we have more grades of A.

Let’s look at the average scores by neighborhood. We have already done so by cuisine.

knitr::include_graphics('AvgViolScoreByZip2013.png')

knitr::include_graphics('AvgViolScoreByZip2014.png')

knitr::include_graphics('AvgViolScoreByZip2015.png')

knitr::include_graphics('AvgViolScoreByZip2016.png')

knitr::include_graphics('AvgViolScoreByZip2017.png')

Over the years, we have a lower Average Score. These NYC inspections must be prompting resturants to improve their facilities and follow regulations!

We decided to see which areas had the highest average number of reinspections. Maybe this can explain the better scores and grades over time.

### DATA GENERATION
library(stringr)
reinspect <- ViolationsData %>% select(-CAMIS, -DBA, -BUILDING, -STREET, -CUISINE.DESCRIPTION, -BORO, -SCORE,
                                           -PHONE, INSPECTION.DATE, -ACTION, 
                                           -VIOLATION.CODE, 
                                           -CRITICAL.FLAG, -GRADE, -GRADE.DATE, 
                                           -RECORD.DATE)
reinspect$year<-factor(as.numeric( format(reinspect$INSPECTION.DATE , '%Y')))
#inspection_types <- INSPECTION.TYPE
reinspect <- reinspect %>%select(-INSPECTION.DATE)  %>% na.omit %>% filter(str_detect(INSPECTION.TYPE, "Re-inspection"))%>%group_by(VIOLATION.DESCRIPTION, year, ZIPCODE )  %>% tally# summarise(re = sum(str_count(reinspect$INSPECTION.TYPE, "Re-inspection")))
knitr::include_graphics('AvgReinspect2013.png')

knitr::include_graphics('AvgReinspect2014.png')

knitr::include_graphics('AvgReinspect2015.png')

knitr::include_graphics('AvgReinspect2016.png')

knitr::include_graphics('AvgReinspect2017.png')

Interestingly enough, the areas with the most reinspections seem to be the ones in which the grades and scores improved. The system of reinspections must be working!

As a last bit of analysis, we decided to help settle a huge dilemna : Where should you get your coffee, Starbucks or Dunking Donuts?

##DATA GENERATION CODE
library(stringr)
ChainssDf<-ViolationsData%>% mutate(isStarbucks=str_detect(DBA,"STARBUCK")) %>% mutate(isDunkin=str_detect(DBA,"DUNKIN"))
ChainssDf$DBA[ChainssDf$isStarbucks==TRUE]<-"STARBUCKS"
ChainssDf$DBA[ChainssDf$isDunkin==TRUE]<-"DUNKIN"
ChainssDf<-ChainssDf%>%filter(DBA %in% c("STARBUCKS","DUNKIN"))
ChainssDf<-ChainssDf%>%select(DBA,BORO,INSPECTION.DATE,ZIPCODE,SCORE,VIOLATION.DESCRIPTION,GRADE)
ChainssDf$year<-factor(as.numeric( format(ChainssDf$INSPECTION.DATE , '%Y')))
ChainssDf<-ChainssDf%>%select(-INSPECTION.DATE)

As you can see, Dunking Donuts has more critical violations than Starbucks in every Borough. Noticible differences are especially see in Brooklyn and Manhattan.

knitr::include_graphics('DS1.png')

Both establishments shared the same top violations. Let’s see what percentage of these violations each has.

knitr::include_graphics('DS2.png')

Once again, Starbucks is the winner!

What about the average score?

knitr::include_graphics('DS3.png')

Regardles of the year, the average score of Dunking Donuts has been higher. The higher the score, the worse. Noticible differences are seen in 2015 and the first three months of 2017.

Let’s see if the Borough your in should impact your choice.

knitr::include_graphics('DS4.png')

I guess when you’re in Brooklyn, it doesn’t matter but everywhere else, stick the Starbucks, especially you Staten Island folks!

Conclusion

Many limitations included the missing data itself. We learned that sometimes, what may seem like an intuitive hypothesis, is actually completely false. There were not as many patterns as we anticipated in terms of Borough. The only thing we did see is that over time, the score and grade has increased, showing us that the inspections are working and that restuarants are improving their fcilities to receieve a better score. According to the website we receved the data frome, three fields are soon to be added as data logic becomes available to populate them accurately. Those fields are VIOLATION POINTS (the points assigned to a violation before or after adjudication, depending on whether adjudication has occurred), FINES TOTAL (the fine amount after adjudication), and DECISION DATE (adjudication date – or date a grade becomes final). With this information, we can do more analysis to see the fines of various violations and learn how each violation actually affects the score. With that, restaurants can focus on the main violations to improve their scores in the next inspection.

LS0tCnRpdGxlOiAiVG8gRWF0IE9yIE5vdCBUbyBFYXQ/IgphdXRob3I6ICJKb25hdGhhbiBHYWxzdXJrYXIgLSBqZmcyMTUwIHwgTGFrc2h5YSBHYXJnIC0gbGcyOTA2IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMjIEludHJvZHVjdGlvbgoKSW4gb25lIG9mIHRoZSBncmVhdGVzdCBjaXRpZXMgaW4gdGhlIHdvcmxkLCBOWUMsIHdlIGhhdmUgYW4gYWJ1bmRhbnQgYW1vdW50IG9mIG9wdGlvbnMgdG8gY2hvb3NlIGZyb20gd2hlbiBpdCBjb21lcyB0byB3aGVyZSB3ZSBlYXQsIHdoZXRoZXIgd2UgYXJlIGxvb2tpbmcgZm9yIGZpbmUgZGluaW5nLCBmYXN0IGZvb2QsIG9yIHNvbWV0aGluZyBpbiBiZXR3ZWVuLiBUaGUgY3Vpc2luZSBjaG9pY2VzIHNlZW0ganVzdCBhcyBlbmRsZXNzLiBJcyB0aGVyZSBhIGdvb2Qgd2F5IHRvIGhlbHAgY2hvb3NlIGEgcmVzdGF1cmFudCB0byBlYXQgaW4gb3IgYSBnZW5lcmFsIGxvY2F0aW9uPyBBbHRob3VnaCBub3QgYW4gZWFzeSB0YXNrLCBvbmUgdGhpbmcgd2UgY2FuIGFsbCBhZ3JlZSBvbiBpcyB0aGF0IG5vYm9keSB3YW50cyB0byBlYXQgaW4gYSByZXN0YXVyYW50IHJpZGRlbiB3aXRoIGhlYWx0aCB2aW9sYXRpb25zLiBJbiBvdXIgcHJvamVjdCwgd2Ugd2lsbCBleHBsb3JlIGFuZCBhbmFseXplIE5ZQ+KAmXMgcmVzdGF1cmFudCBpbnNwZWN0aW9uIHJlc3VsdHMgZnJvbSAyMDEzLTIwMTcgb2YgdGhlIGZpdmUgYm9yb3VnaHMuCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiBhbnN3ZXJpbmc6CgoxLiBXaGljaCBjdWlzaW5lcyBoYXZlIHRoZSBsZWFzdCBhbmQgbW9zdCB2aW9sYXRpb25zIGFsb25nIHdpdGggdGhlaXIgYXNzb2NpYXRlZCBzY29yZS4KCjIuIFdoYXQgcGFydHMgb2YgTllDIGhhdmUgdGhlIGxlYXN0IGFuZCBtb3N0IHZpb2xhdGlvbnM/CgozLiBXZSBoeXBvdGhlc2l6ZSB0aGF0IGxvY2F0aW9uIGlzIGhpZ2hseSBhc3NvY2lhdGVkIHdpdGggaW5zcGVjdGlvbiBncmFkZSBhbmQgc28gd2Ugd2lsbCBiZSBzZWFyY2hpbmcgZm9yIHBhdHRlcm5zIGJldHdlZW4gdGhlc2UgdmFyaWFibGVzLgoKNC4gV2UgYXJlIGFsc28gaW50ZXJlc3RlZCBpbiBzZWVpbmcgaG93IGluc3BlY3Rpb24gZ3JhZGUgYW5kIHNjb3JlIGNoYW5nZXMgb3ZlciB0aW1lICh5ZWFycykgYmFzZWQgdXBvbiBjdWlzaW5lIGFuZCBsb2NhdGlvbi4KCjUuIEEgZGVzY3JpcHRpb24gb2YgdmlvbGF0aW9ucyBpcyBhbHNvIHByb3ZpZGVkIGluIHRoZSBkYXRhIGFuZCB3ZSB3b3VsZCBsaWtlIHRvIGJldHRlciB1bmRlcnN0YW5kIHRoZSBjb21tb24gY2F1c2VzIG9mIHRob3NlIHZpb2xhdGlvbnMgYmFzZWQgdXBvbiB0aGUgZGVzY3JpcHRpb24uCgo2LiBJcyB0aGVyZSBhIHBhdHRlcm4vdHJlbmQgaW4gdmlvbGF0aW9ucy9pbnNwZWN0aW9uIHNjb3JlL2dyYWRlIGJhc2VkIHVwb24gcmVzdGF1cmFudCBjaGFpbnM/CgpXZSBmb3VuZCB0aGlzIGRhdGEgc2V0IGJ5IGV4cGxvcmluZyBOWUMgb3BlbiBkYXRhIHNldHMuIE91ciBsb3ZlIGZvciBmb29kIGFuZCBoZWFsdGggbWFkZSB0aGlzIGEgZ3JlYXQgb3B0aW9uLiBPbmUgb2Ygb3VyIHRlYW0gbWVtYmVycyB3aG8gcmVjZW50bHkgbW92ZWQgdG8gTllDIGlzIGhpZ2hseSBzZWxlY3RpdmUgYWJvdXQgdGhlIGZvb2Qgc2hlIGVhdHMgZHVlIHRvIGhlciByYXJlIGhlYWx0aCBjb25kaXRpb24gYW5kIGhlbmNlLCBoYXZpbmcgdGhlIGtpbmRzIG9mIHF1ZXN0aW9ucyBtZW50aW9uZWQgYWJvdmUgYW5zd2VyZWQgY2FuIHByb3ZlIHRvIGJlIGEgZ3JlYXQgYXNzZXQgdG8gYm90aCBoZXIgYW5kIG90aGVycyBzdWZmZXJpbmcgZnJvbSBoZWFsdGggY29uZGl0aW9ucyBvciB3aG8gbWF5IGp1c3QgYmUgcGlja3kgZWF0ZXJzISBUaGUgZGF0YSBjYW4gYmUgZm91bmQgYW5kIGRvd25sb2FkZWQgaGVyZTogaHR0cHM6Ly9kYXRhLmNpdHlvZm5ld3lvcmsudXMvSGVhbHRoL0RPSE1ILU5ldy1Zb3JrLUNpdHktUmVzdGF1cmFudC1JbnNwZWN0aW9uLVJlc3VsdHMveHg2Ny1rdDU5CgpUbyBkb3dubG9hZCwgY2xpY2sgdGhlIGV4cG9ydCBidXR0b24gb24gdGhlIHJpZ2h0IC0+IERvd25sb2FkIGFzIC0+IGNob29zZSB5b3VyIGZvcm1hdC4gRm9yIHRoaXMgYXNzaWduZW1lbnQgd2UgdXNlZCB0aGUgQ1NWIGZvcm1hdC4KCiMjIFRlYW0KCiMjIyMjIFRlYW0gTWVtYmVyczoKKyBKb25hdGhhbiBHYWxzdXJrYXIKKyBMYWtzaHlhIEdhcmcKCiMjIyMjIFRhc2sgRGlzdHJpYnV0aW9uCkdldCB0YWJsZSBmb3JtYXQgZm9yIHRoaXMKCiMjIEFuYWx5c2lzIG9mIERhdGEgUXVhbGl0eQpUbyBiZWdpbiBvdXIgYW5hbHlzaXMgb2YgZGF0YSBxdWFsaXR5LCBsZXQncyBsb2FkIG91ciBkYXRhLgpgYGB7cn0KVmlvbGF0aW9uc0RhdGEgPC0gcmVhZC5jc3YoZmlsZT0iaW5zcGVjdGlvbi5jc3YiLCBoZWFkZXI9VFJVRSwgc2VwPSIsIiwgYXMuaXM9VFJVRSkKYGBgCgpJbiBvcmRlciB0byBtb3JlIGVhc2lseSB3b3JrIHdpdGggb3VyIGRhdGEgbGV0J3MgZW5zdXJlIHRoYXQgZXZlcnkgZGF0ZSBpcyBpbiBhIGRhdGUgZm9ybWF0IHJhdGhlciB0aGFuIGEgc3RyaW5nIGFuZCBldmVyeSBzdHJpbmcgaXMgYSBmYWN0b3IgdmFyaWFibGUuCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQojI0RhdGUgY29udmVyc2lvbgpWaW9sYXRpb25zRGF0YSA8LSBWaW9sYXRpb25zRGF0YSAlPiUKICBtdXRhdGUoSU5TUEVDVElPTi5EQVRFPSBhcy5EYXRlKElOU1BFQ1RJT04uREFURSwgZm9ybWF0PSAiJW0vJWQvJVkiKSklPiUKICBtdXRhdGUoR1JBREUuREFURT0gYXMuRGF0ZShHUkFERS5EQVRFLCBmb3JtYXQ9ICIlbS8lZC8lWSIpKSU+JQogIG11dGF0ZShSRUNPUkQuREFURT0gYXMuRGF0ZShSRUNPUkQuREFURSwgZm9ybWF0PSAiJW0vJWQvJVkiKSkKIyNGYWN0b3IgY29udmVyc2lvbgpWaW9sYXRpb25zRGF0YSA8LSBWaW9sYXRpb25zRGF0YSAlPiUKICBtdXRhdGUoQ1VJU0lORS5ERVNDUklQVElPTj0gYXMuZmFjdG9yKENVSVNJTkUuREVTQ1JJUFRJT04pKSU+JQogIG11dGF0ZShCT1JPPSBhcy5mYWN0b3IoQk9STykpJT4lCiAgbXV0YXRlKFZJT0xBVElPTi5DT0RFPSBhcy5mYWN0b3IoVklPTEFUSU9OLkNPREUpKSU+JQogIG11dGF0ZShDUklUSUNBTC5GTEFHPSBhcy5mYWN0b3IoQ1JJVElDQUwuRkxBRykpJT4lCiAgbXV0YXRlKEdSQURFPSBhcy5mYWN0b3IoR1JBREUpKSU+JQogIG11dGF0ZShJTlNQRUNUSU9OLlRZUEU9IGFzLmZhY3RvcihJTlNQRUNUSU9OLlRZUEUpKQpgYGAKCgpNYW55IG9mIHRoZSBxdWVzdGlvbnMgd2UgYXJlIGludGVyZXN0ZWQgaW4gYW5zd2VyaW5nIGludm9sdmUgdHJlbmRzIGFjcm9zcyByZXN0dWFyYW50IGxvY2F0aW9ucy4gV2UgZmlyc3QgY2hlY2tlZCB0byBzZWUgdGhlIG51bWJlciBvZiByZXN0dWFyYW50IGluc3BlY3Rpb25zIGJ5IGJvcm91Z2guIEZyb20gdGhlIHBsb3QgYmVsb3csIHdlIG5vdGljZWQgdGhhdCB0aGVyZSB3ZXJlIGEgbnVtYmVyIG9mIGluc3BlY3Rpb25zIGluIHdoaWNoIHRoZSBCb3JvdWdoIGluZm9ybWF0aW9uIHdhcyBtaXNzaW5nIGFuZCB3b24ndCBoZWxwIG91ciBhbmFseXNpcy4KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKYm9yb3VnaFBsb3QgPC0gZ2dwbG90KFZpb2xhdGlvbnNEYXRhLCBhZXMoQk9STywgZmlsbD1CT1JPKSkKYm9yb3VnaFBsb3QgKyBnZW9tX2JhcigpKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgZ2d0aXRsZSgnSW5zcGVjdGlvbiBDb3VudCBieSBCb3JvdWdoJykgKyAKICBsYWJzKHggPSAiQm9yb3VnaCIsIHkgPSJOdW1iZXIgb2YgSW5zcGVjdGlvbnMiKQpgYGAKCkFub3RoZXIgbWFpbiBmZWF0dXJlIG9mIG91ciBkYXRhIHNldCBpcyB0aGUgaW5zcGVjdGlvbiB5ZWFyIHNpbmNlIHdlIHdpc2ggdG8gZXhwbG9yZSBwYXR0ZXJzIGluIGluc3BlY3Rpb24gZ3JhZGVzL3Njb3JlcyBvdmVyIHRoZSB5ZWFycy4gRnJvbSB0aGUgcGxvdCBiZWxvdywgd2Ugbm90aWNlZCB0aGF0IHRoZXJlIGlzIGFsbW9zdCBubyBpbnNwZWN0aW9uIGRhdGEgYmVmb3JlIDIwMTMgYW5kIHN1cnByaXNpbmdseSBtb3JlIGluc3BlY3Rpb24gZGF0YSBpbiAxOTAwIHRoYW4gMjAxMiBhbmQgMjAxMS4gV2UgZGVjaWRlZCB0byBvbmx5IHdvcmsgd2l0aCBkYXRhIGZyb20gMjAxMyBhbmQgdXAuIEl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIG51bWJlciBvZiBpbnNwZWN0aW9ucyBmb3IgMjAxNyBpcyBsb3cgc2luY2UgdGhlIDIwMTcgZGF0YSBpcyBvbmx5IGF2YWlsYWJsZSBmcm9tIEphbnVhcnkgLSBNYXJjaC4KYGBge3J9CnZpb2xhdGlvblllYXJzUGxvdCA8LSBnZ3Bsb3QoVmlvbGF0aW9uc0RhdGEsIGFlcyhmYWN0b3IoYXMubnVtZXJpYyhmb3JtYXQoSU5TUEVDVElPTi5EQVRFLCAnJVknKSkpLCBmaWxsPSJSZWQiKSkKdmlvbGF0aW9uWWVhcnNQbG90ICsgZ2VvbV9iYXIoKStjb29yZF9mbGlwKCkrIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICt4bGFiKCJJbnNwZWN0aW9uIFllYXIiKSt5bGFiKCJJbnNwZWN0aW9uIENvdW50cyIpICsgZ2d0aXRsZSgiSW5zcGVjdGlvbiBDb3VudCBieSBZZWFyIikKYGBgCgpBbm90aGVyIGNydWNpYWwgZmVhdHVyZSBvZiBvdXIgZGF0YSBzZXQgaXMgZ3JhZGUgYSByZXN0YXVyYW50IHJlY2VpdmVkIGFmdGVyIGluc3BlY3Rpb24uIFdlIGRlY2lkZWQgdG8gcGxvdCBhIHN0YWNrZWQgYmFyIGNoYXJ0IGZvciB0byBzZWUgdGhlIGNvdW50IG9mIGVhY2ggdHlwZSBvZiBncmFkZSBmb3IgZXZlcnkgY3Vpc2luZS4gV2UgdXNlZCBhIHN0YWNrZWQgYmFyIGNoYXJ0IGJlY2F1c2Ugd2Ugd2FudGVkIHRvIHF1aWNrbHkgYXNzZXNzIHRoZSBtYWduaXR1ZGUgbWlzc2luZyB3aXRob3V0IHRha2luZyB1cCBleHRyYSByb29tLiBGcm9tIHRoZSBwbG90IGJlbG93LCBpdCB3YXMgc2hvY2tpbmcgdG8gc2VlIHRoYXQgd2UgZ2VuZXJhbGx5IGhhZCBtb3JlIG1pc3NpbmcgZ3JhZGVzIHRoYW4gZ3JhZGVzLiBUaGlzIHdhcyB0cnVlIHJlZ2FyZGxzcyBvZiBjdWlzaW5lLiBJdCB3YXMgYWxzbyBpbnRlcmVzdGVkIHRoYXQgdGhlIGdyYWRlcyB3ZXJlIHB1cmVseSBtaXNzaW5nIGFuZCBub3QgY2F0ZWdvcml6ZWQgYXMgIk5vdCBZZXQgR3JhZGVkIi4KYGBge3IsZmlnLmhlaWdodD0xMn0KZ2dwbG90KFZpb2xhdGlvbnNEYXRhLCBhZXMoQ1VJU0lORS5ERVNDUklQVElPTiwgZmlsbCA9IEdSQURFKSkgKyBnZW9tX2JhcigpICsgCiAgY29vcmRfZmxpcCgpK2dndGl0bGUoInN0YWNrZWQgYmFyIGNoYXJ0IG9mIGdyYWRlIGRpc3RyaWJ1dGlvbnMgYWNyb3NzIGN1aXNpbmVzIikKYGBgCgpUbyBzdGF5IG9uIHRoZSB0b3BpYyBvZiBncmFkZXMsIGFmdGVyIHJlc2VhcmNoaW5nIHRoZSBsZXR0ZXIgZ3JhZGluZyBwcm9ncmFtLCB3ZSBmb3VuZCB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uOiAKKyBBIHNjb3JlIG9mIGxlc3MgdGhhbiAxNCBwb2ludHMgb24gZWl0aGVyIGluaXRpYWwgb3IgcmUtaW5zcGVjdGlvbiByZXN1bHRzIGluIGFuIOKAnEHigJ0gZ3JhZGUKKyBPbiByZS1pbnNwZWN0aW9uLCBhIHNjb3JlIG9mIDE0LTI3IHBvaW50cyBtZWFucyBhIHJlc3RhdXJhbnQgcmVjZWl2ZXMgYm90aCBhIOKAnELigJ0gZ3JhZGUgYW5kIGEg4oCcR3JhZGUgUGVuZGluZ+KAnSBjYXJkLgorIE9uIHJlLWluc3BlY3Rpb24sIGEgc2NvcmUgb2YgMjggb3IgbW9yZSBwb2ludHMgbWVhbnMgYSByZXN0YXVyYW50IHJlY2VpdmVzIGJvdGggYSDigJxD4oCdIGdyYWRlIGFuZCBhIOKAnEdyYWRlIFBlbmRpbmfigJ0gY2FyZC4KCkJvdGggWiBhbmQgUCByZXByZXNlbnQgZ3JhZGUgcGVuZGluZywgaG93ZXZlciBQIHJlcHJlc2VudHMgYSBHcmFkZSBQZW5kaW5nIGlzc3VlZCBvbiByZS1vcGVuaW5nIGZvbGxvd2luZyBhbiBpbml0aWFsIGluc3BlY3Rpb24gdGhhdCByZXN1bHRlZCBpbiBhIGNsb3N1cmUuCgpXZSBhbHNvIGRpc2NvdmVyZWQgdGhhdCBub3QgZXZlcnkgaW5zcGVjdGlvbiBpcyAiZ3JhZGFibGUiLiBHcmFkYWJsZSBpbnNwZWN0aW9ucyBoYXZlIHRoZSBmb2xsb3dpbmcgcHJvcGVydGllczoKCisgSU5TUEVDVElPTiBUWVBFIGluIChDeWNsZSBJbnNwZWN0aW9uL0luaXRpYWwgSW5zcGVjdGlvbiwgQ3ljbGUgSW5zcGVjdGlvbi9SZS1JbnNwZWN0aW9uLCBQcmUtUGVybWl0IChPcGVyYXRpb25hbCkvSW5pdGlhbCBJbnNwZWN0aW9uLCBQcmUtUGVybWl0IChPcGVyYXRpb25hbCkvUmUtSW5zcGVjdGlvbikKKyBBQ1RJT04gaW4gKFZpb2xhdGlvbnMgd2VyZSBjaXRlZCBpbiB0aGUgZm9sbG93aW5nIGFyZWEocyksIE5vIHZpb2xhdGlvbnMgd2VyZSByZWNvcmRlZCBhdCB0aGUgdGltZSBvZiB0aGlzIGluc3BlY3Rpb24sIEVzdGFibGlzaG1lbnQgQ2xvc2VkIGJ5IERPSE1IKQorIElOU1BFQ1RJT04gREFURSA+IEp1bHkgMjYsIDIwMTAKClRoaXMgY2FuIHByb2JhYmx5IGV4cGxhaW4gYSBmYWlyIGFtb3VudCBvZiB0aGUgbWlzc2luZyBncmFkZSBkYXRhIG9ic2VydmVkIGluIG91ciBwbG90LgoKQWNjb3JkaW5nIHRvIHRoZSBBQk9VVCB0aGUgZGF0YSBzZXQgcGFnZTogVGhlIFNDT1JFIGFuZCBHUkFERSBmaWVsZHMgbWF5IGJlIGluY29uc2lzdGVudCB3aXRoIGVhY2ggb3RoZXIgYmVjYXVzZSBvZiBsaW1pdGF0aW9ucyBvciBlcnJvcnMgaW4gdGhlIGRhdGEgc3lzdGVtcy4gVGhhdCBpcyB0byBzYXksIHNjb3JlcyBvZiAwLTEzLCAxNC0yNyBhbmQgMjgrIGFyZSBub3QgYWx3YXlzIGFjY29tcGFuaWVkIGJ5IEEsIEIgYW5kIEMgZ3JhZGVzLCByZXNwZWN0aXZlbHksIHdoZW4gdGhleSBzaG91bGQgYmUuIFRoZXJlIG1heSBhbHNvIGJlIGNhc2VzIHdoZXJlIGEgZ3JhZGUgY2FyZCB3YXMgZ2l2ZW4gb3V0IGJ1dCBhIHJlY29yZCBvZiB0aGF0IGdyYWRlIGlzc3VhbmNlIGlzIG1pc3NpbmcgZnJvbSB0aGUgZGF0YSBzeXN0ZW0sIGFuZCB0aGVyZWZvcmUgbWlzc2luZyBmcm9tIHRoaXMgZGF0YXNldCwgZXZlbiB0aG91Z2ggdGhlIFNDT1JFIGZpZWxkIGlzIHBvcHVsYXRlZC4gIE5vdGUgdGhhdCB3aGVuIGluaXRpYWwgaW5zcGVjdGlvbnMgYXJlIGFkanVkaWNhdGVkIGRvd24gdG8gdGhlIEEgcmFuZ2UsIHRoZSBhYnNlbmNlIG9mIGFuIGFjY29tcGFueWluZyBncmFkZSBhc3NvY2lhdGVkIHdpdGggdGhhdCBpbnNwZWN0aW9uIGlzIGNvcnJlY3QsIGJlY2F1c2UgdGhlIGdyYWRlIHdvdWxkIG5vdCBiZSBhc3NpZ25lZCB1bnRpbCB0aGUgcmUtaW5zcGVjdGlvbiBpcyBwZXJmb3JtZWQuIAoKVG8gZ2FpbiBzb21lIGZpbmFsIGluc2lnaHQgb24gdGhlIGRhdGEgcXVhbGl0eSwgd2UgZGVjaWRlZCB0byBwbG90IHRoZSByZWxhdGlvbnNocCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgbWlzc2luZyBzY29yZXMgYnkgZ3JhZGUgYW5kIGJ5IHdoZXRoZXIgb3Igbm90IGEgdmlvbGF0aW9uIHdhcyByZXBvcnRlZC4gV2UgdHJhbnNmb3JtZWQgdGhlIGFjdGlvbnMgdGFrZW4gaW50byB0aHJlZSBjYXRlZ29yaWVzOgoxLiBObyB2aW9sYXRpb25zIHdlcmUgcmVjb3JkZWQgYXQgdGhlIHRpbWUgb2YgdGhpcyBpbnNwZWN0aW9uIHRvIE5vIFZpb2xhdGlvbgoyLiBBbnkgYWN0aW9uIHJlcG9ydGVkIHRvIFZpb2xhdGlvbiByZXBvcnRlZAozLiBNaXNzaW5nIGFjdGlvbnMgdG8gTkEKV2UgdGhlbiBjb3VudGVkIGlmIHRoZSBzY29yZSB3YXMgcHJvdmlkZWQgb3Igbm90LgoKYGBge3IsZmlnLmhlaWdodD00fQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpzY29yZV9ncmFkZSA8LSBWaW9sYXRpb25zRGF0YVsgLWMoMTo5LCAxMToxMywgMTY6MTgpIF0Kc2NvcmVfZ3JhZGVbc2NvcmVfZ3JhZGUgPT0gJyddIDwtIE5BCnNjb3JlX2dyYWRlX2NvbWJvcyA8LSBzY29yZV9ncmFkZSAgJT4lIG11dGF0ZShtaXNzaW5nX3Njb3JlID0gaWZlbHNlKGlzLm5hKFNDT1JFKSwgInllcyIsICJubyIpKQpzY29yZV9ncmFkZV9taXNzaW5nIDwtIGNvdW50KHNjb3JlX2dyYWRlX2NvbWJvcywgYygnR1JBREUnLCAnbWlzc2luZ19zY29yZScsICdBQ1RJT04nKSkKc2NvcmVfZ3JhZGVfbWlzc2luZyA8LSBzY29yZV9ncmFkZV9taXNzaW5nICU+JSAKICBtdXRhdGUodmlvbGF0aW9uID0gaWZlbHNlKEFDVElPTiA9PSAnTm8gdmlvbGF0aW9ucyB3ZXJlIHJlY29yZGVkIGF0IHRoZSB0aW1lIG9mIHRoaXMgaW5zcGVjdGlvbi4nLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vIHZpb2xhdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKHNjb3JlX2dyYWRlX21pc3NpbmckQUNUSU9OKSwgTkEsICJWaW9sYXRpb24gUmVwb3J0ZWQiKSkpICU+JQogIHNlbGVjdCgtYyhBQ1RJT04pKQpnZ3Bsb3Qoc2NvcmVfZ3JhZGVfbWlzc2luZywgYWVzKHggPSBHUkFERSwgeSA9IGxvZyhmcmVxKSwgZmlsbCA9IG1pc3Npbmdfc2NvcmUpKSArIAogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBwb3NpdGlvbiA9ICdkb2RnZScpICsgZmFjZXRfd3JhcCh+dmlvbGF0aW9uKSArIAogIGdndGl0bGUoIkdyYWRlIGFuZCBtaXNzaW5nIHNjb3JlIGNvbWJpbmF0aW9ucyBieSB2aW9sYXRpb24gcmVwb3J0IikgKwogIGxhYnMoeCA9ICJHcmFkZSIsIHkgPSAiTG9nKEZyZXF1ZW5jeSkiKQpgYGAKSW50ZXJlc3RpbmcgaW5zaWdodHMgZnJvbSBwbG90IGFib3ZlIGFyZToKCjEuIEdyYWRlcyBvZiBOQSByZXBvcnRlZCBoaWdoIGZyZXF1ZW5jeSBvZiBzY29yZSBpbiBib3RoIE5vIHZpb2xhdGlvbiBhbmQgdmlvbGF0aW9uIHJlcG9ydGVkIGNhdGVnb3J5LgoKMi4gQXQgZmlyc3QsIGl0IGFwcGVhcnMgdGhhdCBoaWdoIHNjb3JlcyBhcmUgcmVsYXRlZCB0byBsb3cgZ3JhZGVzIG9yIG5lZWRzIGdyYWRpbmcgYnV0IHRoZW4gd2UgZmluZCByZXN0YXVyYW50cyB3aXRoIGEgZ3JhZGUgb2YgQSB0aGF0IGhhcyB0aGUgc2FtZSBzY29yZSBhcyBhIHJlc3RhdXJhbnQgd2l0aCBhIGdyYWRlIG9mIEMuCgozLiBBbm90aGVyIGluc2lnaHQgYnkganVzdCBsb29raW5nIGF0IHRoZSBkYXRhIGlzIHdlIHN1cnByaXNpbmdseSBzYXcgdGhhdCByZXN0YXVyYW50cyB3aXRoIGEgY3JpdGljYWwgZmxhZyBzdGlsbCByZWNlaXZlIGdyYWRlcyBvZiBBLlRoaXMgbmVlZHMgZnVydGhlciBhbmFseXNpcy4gU0hPVUxEIFdFIE1BS0UgQSBQTE9UIEZPUiBUSElTPz8/PwoKIyMgRXhlY3V0aXZlIFN1bW1hcnkKTllDIHB1dHMgYSBsb3Qgb2YgdGltZSBhbmQgbW9uZXkgaW50byBpbnNwZWN0aW5nIHJlc3R1YXJhbnRzLiBXaHkgd291bGQgdGhleSBnbyB0aHJvdWdoIGFsbCB0aGlzIHRyb3VibGU/IFRvIGVuc3VyZSBxdWFsaXR5IG1lYWxzIGFuZCBzYXRpc2ZhY3Rpb24gb2YgTllDIHJlc2lkZW50cyBvZiBjb3Vyc2UhIEl0IGlzIGltcG9ydGFudCBmb3IgcmVzdGF1cmFudHMgYW5kIGVzdGFibGlzaG1lbnQgdGhhdCBtZWV0IGF0IGxlYXN0IHRoZSBtaW5pbXVtIHJlcXVpcmVtZW50cyBvZiBoZWFsdGggYW5kIHNhZmV0eSByZWd1bGF0aW9ucyBpbiBvcmRlciB0byBwcm9tb3RlIGxlc3MgZm9vZCBzaWNrIHJlc2lkZW50cyBhbmQgYSBjbGVhbmVyIE5ZQy4gCgpIb3cgY2FuIHdlIHRlbGwgaWYgdGhlc2UgaW5zcGVjdGlvbnMgYXJlIGFjdHVhbGx5IHdvcmtpbmcgdG8gcHJvbW90ZSByZXN0dWFyYW50cyB0byBtZWV0IHJlZ3VsYXRpb25zPyBMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgcHJvcG9ydGlvbiBvZiBzY29yZXMgdGhhdCB3ZXJlIGdyYWRlZCBhbiBBIGluIDIwMTMgYW5kIHRoZSBwcm9wb3J0aW9uIG9mIHNjb3JlcyB0aGF0IHdlcmUgZ3JhZGVkIGFuIEEgaW4gMjAxNi4gTGV0J3MgdmlldyB0aGlzIGluZm9ybWF0aW9uIGJ5IGxvY2F0aW9uIHNvIHRoYXQgeW91IGtub3cgd2hhdCBuZWlnaGJvcmhvb2QgdG8gY2hvb3NlIHdoZW4geW91J3JlIGNyYXZpbmcgYSByZXN0YXVyYW50LgoKYGBge3IgZWNobyA9IEZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQXByb3A1LnBuZycpCmBgYAoKYGBge3IgZWNobyA9IEZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQXByb3AyLnBuZycpCmBgYAoKVGhlIGRhcmtlciB0aGUgc2hhZGUgb2YgcmVkLCB0aGUgaGlnaGVyIHByb3BvcnRpb24gb2YgQSBncmFkZXMuIEl0IGlzIGNsZWFyIHRoYXQgb3ZlciB0aGUgbGFzdCAzIHllYXJzLCB0aGUgcHJvcG9ydGlvbiBvZiByZXN0dWFyYW50cyB3aXRoIGEgZ3JhZGUgb2YgYW4gQSBoYXMgaW5zY3JlYXNlZC4gTllDIGluc3BlY3Rpb25zIG11c3QgYmUgd29ya2luZyB0byBpbXByb3ZlIHRoZSBxdWFsaXR5IG9mIHRoZSByZXN0dWFyYW50cyB3ZSBlYXQgaW4hIEluIDIwMTMsIGl0IHNlZW1zIHRoYXQgdGhlIHRoZXJlIHdlcmUgb25seSBhIGZldyBuZWlnaGJvcmhvb2RzIHdpdGggYW4gZXh0cmVtZWx5IGhpZ2ggcHJvcG9ydGlvbiBvZiBBIGdyYWRlcy4gT25seSAzIGFyZWFzIGluIFN0YXRlbiBJc2xhbmQsIDEgaW4gTG9uZyBJc2xhbmQsIGEgZmV3IGluIEJyb29rbHluLCBNYW5oYXR0YW4sIGFuZCB0aGUgQnJvbnguIEluIDIwMTYgb24gdGhlIG90aGVyIGhhbmQsIHRoZSBwcm9wb3J0aW9uIG9mIEEgZ3JhZGVzIGlzIHZlcnkgaGlnaCBhbG1vc3QgcmVnYXJkbGVzIG9mIG5laWdoYm9yaG9vZC4gS2VlcCBpdCB1cCBOWUMgcmVzdGFydWFudCBpbnNwZWN0aW9ucyEKClRPIHNlZSB0aGlzIGZyb20gYW5vdGhlciBhbmdsZSwgbGV0J3MgbG9vayBhdCB0aGUgYXZlcmFnZSBzY29yZXMgcGVyIG5laWdoYm9yaG9vZCBpbiAyMDEzIGFuZCAyMDE2LiBUSEVZIFdFUkUgQklOTkVEIFRISVMgV0FZSkZESEpLSEZMU0RGRlNECgpgYGB7ciBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBdmdWaW9sU2NvcmVCeVppcDIwMTMucG5nJykKYGBgCgpgYGB7ciBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBdmdWaW9sU2NvcmVCeVppcDIwMTYucG5nJykKYGBgCgpKdXN0IGxvb2sgaG93IG11Y2ggbGlnaHRlciB0aGUgY29sb3Igb2YgZWFjaCBuZWlnaGJvcmhvb2QgZ290ISBMaWdodGVyIGNvbG9ycyBtZWFuIGEgbG93ZXIgYXZlcmFnZSBzY29yZSwgd2hpY2ggY29ycmVsYXRlcyB0byBsZXNzIHZpb2xhdGlvbnMgYW5kIGEgaGVhbHRoaWVyIGVzdGFibGlzaG1lbnQhCgpXZWxsIG5vdyB3ZSBrbm93IHdoYXQgbmVpZ2hib3Job29kcyB0byBiZSBpbiBmb3IgZXN0YWJsaXNobWVudHMgd2l0aCBsb3cgc2NvcmVzIGFuZCBBIGdyYWRlcywgYnV0IHdoYXQgYXJlIHRoZSBhdmVyYWdlIHNjb3JlcyBiYXNlZCB1cG9uIGN1aXNpbmU/CgpgYGB7ciBlY2hvID0gRkFMU0V9CmF2ZXJhZ2VfY3Vpc2luZSA8LSBWaW9sYXRpb25zRGF0YSAlPiUgIGdyb3VwX2J5KENVSVNJTkUuREVTQ1JJUFRJT04pICU+JSBuYS5vbWl0ICU+JQogIHN1bW1hcml6ZShhdmVyYWdlX3Njb3JlID0gbWVhbihTQ09SRSkpCmF2ZXJhZ2VfY3Vpc2luZSA8LSBhcnJhbmdlKGF2ZXJhZ2VfY3Vpc2luZSwgLWF2ZXJhZ2Vfc2NvcmUpCgphdmVyYWdlX2N1aXNpbmUgPC0gYXZlcmFnZV9zY29yZXMgJT4lICBncm91cF9ieShDVUlTSU5FLkRFU0NSSVBUSU9OKSAlPiUgbmEub21pdCAlPiUKICBzdW1tYXJpemUoYXZlcmFnZV9zY29yZSA9IG1lYW4oU0NPUkUpKQphdmVyYWdlX2N1aXNpbmUgPC0gYXJyYW5nZShhdmVyYWdlX2N1aXNpbmUsIC1hdmVyYWdlX3Njb3JlKQpgYGAKCmBgYHtyIGVjaG89RkFMU0V9Cm4gPC0gbnJvdyhhdmVyYWdlX2N1aXNpbmUpCmJlc3QxMCA8LSBhdmVyYWdlX2N1aXNpbmVbKG4tIDEwKTpuLF0KYmVzdDEwIDwtIGJlc3QxMFtvcmRlcihiZXN0MTAkYXZlcmFnZV9zY29yZSksXQoKZ2dwbG90KGJlc3QxMCwgYWVzKHJlb3JkZXIoeCA9IENVSVNJTkUuREVTQ1JJUFRJT04sIC1hdmVyYWdlX3Njb3JlKSwgYXZlcmFnZV9zY29yZSwgZmlsbCA9IENVSVNJTkUuREVTQ1JJUFRJT04pKSArIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpK2dndGl0bGUoIkF2ZXJhZ2Ugc2NvcmUgb2YgdG9wIDEwIGN1aXNpbmVzIikreGxhYigiQ3Vpc2luZSBkZXNjcmlwdGlvbiIpK3lsYWIoImF2ZXJhZ2Ugc2NvcmUiKStnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4yZiIsIGF2ZXJhZ2Vfc2NvcmUpKSxwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMSksI3Bvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLjUsIHNpemUgPSA0KQpgYGAKCmBgYHtyIGVjaG8gPSBGQUxTRX0Kd29yc3QxMCA8LSBhdmVyYWdlX2N1aXNpbmVbMToxMCxdCgpnZ3Bsb3Qod29yc3QxMCwgYWVzKHJlb3JkZXIoeCA9IENVSVNJTkUuREVTQ1JJUFRJT04sIC0tYXZlcmFnZV9zY29yZSksIGF2ZXJhZ2Vfc2NvcmUsIGZpbGwgPSBDVUlTSU5FLkRFU0NSSVBUSU9OKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgY29vcmRfZmxpcCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKStnZ3RpdGxlKCJBdmVyYWdlIHNjb3JlIG9mIFdvcnN0IDEwIGN1aXNpbmVzIikreGxhYigiQ3Vpc2luZSBkZXNjcmlwdGlvbiIpK3lsYWIoImF2ZXJhZ2Ugc2NvcmUiKStnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4yZiIsIGF2ZXJhZ2Vfc2NvcmUpKSxwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpKQpgYGAKCkl0IGlzIGludGVyZXN0aW5nIHRoYXQgTm90IExpc3RlZC9Ob3QgQXBwbGljYWJsZSBhbmQgT3RoZXIgbWFkZSBpdCB0byB0aGUgdG9wIDEwIGJ1dCB0aGVyZSB5b3UgaGF2ZSBpdCwgdGhlIGJlc3QgYW5kIHdvcnN0IGN1aXNpbmVzIGJhc2VkIHVwb24gdGhlaXIgYXZlcmFnZSBzY29yZXMuCgojIyBNYWluIEFuYWx5c2lzCkFmdGVyIGFuYWx5emluZyB0aGUgcXVhbGl0eSBvZiB0aGUgZGF0YSBzZXQsIHdlIGdvdCByaWQgb2YgZGF0YSBpbiB3aGljaCB0aGUgQm9yb3VnaCBpcyBtaXNzaW5nIGFuZCB0aGUgeWVhciBpcyBiZWZvcmUgMjAxMy4KCmBgYHtyfQpWaW9sYXRpb25zRGF0YSA8LSBWaW9sYXRpb25zRGF0YSAlPiUgZmlsdGVyIChCT1JPICE9ICJNaXNzaW5nIikKVmlvbGF0aW9uc0RhdGEgPC0gVmlvbGF0aW9uc0RhdGEgJT4lIGZpbHRlcigKICBhcy5udW1lcmljKGZvcm1hdChJTlNQRUNUSU9OLkRBVEUgLCAnJVknKSkgPiAyMDEyKQpgYGAKCk5vdyB3ZSBjYW4gZ2V0IGEgYmV0dGVyIHBpY3R1cmUgb2YgdGhlIHRvdGFsIG51bWJlciBvZiBpbnNwZWN0aW9ucyBieSB5ZWFyIGFuZCBib3JvdWdoLgpgYGB7cn0KYm9yb3VnaFBsb3QgPC0gZ2dwbG90KFZpb2xhdGlvbnNEYXRhLCBhZXMoQk9STyxmaWxsPUJPUk8pKQpib3JvdWdoUGxvdCArIGdlb21fYmFyKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIGZhY2V0X3dyYXAofmZhY3Rvcihhcy5udW1lcmljKCBmb3JtYXQoSU5TUEVDVElPTi5EQVRFICwgJyVZJykpKSkgKyAKICBnZ3RpdGxlKCJUb3RhbCBJbnNwZWN0aW9ucyBmcm9tIDIwMTMtMjAxNyBpbiBlYWNoIEJvcm91Z2giKSArCiAgeGxhYigiQm9yb3VnaCIpICsgeWxhYigiTnVtYmVyIG9mIEluc3BlY3Rpb25zIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCmBgYApBZnRlciAyMDEzLCB0aGVyZSBzZWVtcyB0byBiZSBhIGNvbnNpc3RlbnQgYW1vdW50IG9mIGluc3BlY3Rpb25zIGFjcm9zIHRoZSB5ZWFycy4gVGhlIG51bWJlciBvZiBpbnNwZWN0aW9ucyBieSBib3JvdWdoIGFsc28gc2VlbXMgdG8gbWFrZSBzZW5zZSBzaW5jZSB3ZSBleHBlY3QgTWFuaGF0dGFuIHRvIGhhdmUgdGhlIGxhcmdlc3QgbnVtYmVyIG9mIHJlc3R1YXJhbnRzLgoKTmV4dCwgd2UgdG9vayBhIGxvb2sgYXQgdGhlIGdyYWRlIGRpc3RyaWJ1dGlvbiBieSBib3JvdWdoLgpgYGB7cixmaWcud2lkdGg9OCxmaWcuaGVpZ2h0PTN9Cmluc3BlY3Rpb25fZ3JhZGVzIDwtIFZpb2xhdGlvbnNEYXRhICU+JSBzZWxlY3QoLUNBTUlTLCAtREJBLCAtQlVJTERJTkcsIC1TVFJFRVQsIC1aSVBDT0RFLCAtUEhPTkUsIC1BQ1RJT04sIC1WSU9MQVRJT04uQ09ERSwgLVZJT0xBVElPTi5ERVNDUklQVElPTiwgLUNSSVRJQ0FMLkZMQUcsIC1TQ09SRSwgLUdSQURFLkRBVEUsIC1SRUNPUkQuREFURSwgLUlOU1BFQ1RJT04uVFlQRSwtQ1VJU0lORS5ERVNDUklQVElPTikKCmluc3BlY3Rpb25fZ3JhZGVzX3dveWVhcjwtaW5zcGVjdGlvbl9ncmFkZXMgJT4lIHNlbGVjdCgtSU5TUEVDVElPTi5EQVRFKQoKaW5zcGVjdGlvbl9ncmFkZXNfd295ZWFyIDwtIGluc3BlY3Rpb25fZ3JhZGVzX3dveWVhciAlPiUgZ2F0aGVyKGtleSwgdmFsdWUsIC1CT1JPKSAlPiUgZ3JvdXBfYnkoQk9STywga2V5LCB2YWx1ZSkgJT4lIHRhbGx5ICU+JSBzcHJlYWQodmFsdWUsIG4sIGZpbGwgPSAwKSMgJT4lIGdhdGhlcihibGFoLCAtQk9STywgLWtleSkgI3N1bW1hcml6ZShhcHJvcCA9IEEvKEErQitDKykpCm5hbWVzKGluc3BlY3Rpb25fZ3JhZGVzX3dveWVhcilbbmFtZXMoaW5zcGVjdGlvbl9ncmFkZXNfd295ZWFyKT09ImtleSJdIDwtICJncmFkZSIKbmFtZXMoaW5zcGVjdGlvbl9ncmFkZXNfd295ZWFyKVtuYW1lcyhpbnNwZWN0aW9uX2dyYWRlc193b3llYXIpPT0iIl0gPC0gInVua25vd24iCmluc3BlY3Rpb25fZ3JhZGVzX3dveWVhciA8LSBpbnNwZWN0aW9uX2dyYWRlc193b3llYXIgJT4lIGdhdGhlcihrZXksIHZhbHVlLCAtQk9STywtZ3JhZGUpClJlbEZyZXE8LWZ1bmN0aW9uKG0pewogICAoKG0gKS9zdW0obSkpCiB9Cmluc3BlY3Rpb25fZ3JhZGVzX3dveWVhcjwtaW5zcGVjdGlvbl9ncmFkZXNfd295ZWFyICU+JSBncm91cF9ieShCT1JPLGdyYWRlKSAlPiUKICAgIG11dGF0ZShwZXJjZW50YWdlID0gKHZhbHVlL3N1bSh2YWx1ZSkpKjEwMCkKZ2dwbG90KGluc3BlY3Rpb25fZ3JhZGVzX3dveWVhciwgYWVzKEJPUk8sIHBlcmNlbnRhZ2UgLGZpbGw9IGtleSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZ3RpdGxlKCclR3JhZGUgZGlzdHJpYnV0aW9uIGFjcm9zcyBib3JvdWdocycpICsgCiAgbGFicyh4ID0gImJvcm91Z2hzIiwgeSA9InBlcmNlbnRhZ2UiKStndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9IkdyYWRlIikpCgpgYGAKV2UgaW5pdGlhbGx5IGh5cG90aGVzaXplZCB0aGF0IHdlIHdvdWxkIHNlZSBhIHBhdHRlcm4gaW4gZ3JhZGUgZGlzdHJpYnV0aW9uIGJ5IGJvcm91Z2gsIGhvd2V2ZXIsIHRoZSBwbG90IHNob3dzIHVzIHRoYXQgZ3JhZGUgZGlzdHJpYnV0aW9ucyBhcmUgYWxtb3N0IHRoZSBzYW1lIHJlZ2FyZGxlc3Mgb2YgYm9yb3VnaC4KCmBgYHtyLGZpZy53aWR0aD04fQoKaW5zcGVjdGlvbl9ncmFkZXNfeWVhcjwtaW5zcGVjdGlvbl9ncmFkZXMlPiUgbXV0YXRlKHllYXI9ZmFjdG9yKGFzLm51bWVyaWMoZm9ybWF0KElOU1BFQ1RJT04uREFURSAsICclWScpKSkpCmluc3BlY3Rpb25fZ3JhZGVzX3llYXI8LWluc3BlY3Rpb25fZ3JhZGVzX3llYXIlPiUgc2VsZWN0KC1JTlNQRUNUSU9OLkRBVEUpCiBpbnNwZWN0aW9uX2dyYWRlc195ZWFyIDwtIGluc3BlY3Rpb25fZ3JhZGVzX3llYXIgJT4lIGdhdGhlcihrZXksIHZhbHVlLCAtQk9STywteWVhcikgJT4lIGdyb3VwX2J5KEJPUk8seWVhciwga2V5LCB2YWx1ZSkgJT4lIAogICB0YWxseSAlPiUgc3ByZWFkKHZhbHVlLCBuLCBmaWxsID0gMCkjICU+JSBnYXRoZXIoYmxhaCwgLUJPUk8sIC1rZXkpICNzdW1tYXJpemUoYXByb3AgPSBBLyhBK0IrQyspKQpuYW1lcyhpbnNwZWN0aW9uX2dyYWRlc195ZWFyKVtuYW1lcyhpbnNwZWN0aW9uX2dyYWRlc195ZWFyKT09ImtleSJdIDwtICJncmFkZSIKbmFtZXMoaW5zcGVjdGlvbl9ncmFkZXNfeWVhcilbbmFtZXMoaW5zcGVjdGlvbl9ncmFkZXNfeWVhcik9PSIiXSA8LSAidW5rbm93biIKaW5zcGVjdGlvbl9ncmFkZXNfeWVhciA8LSBpbnNwZWN0aW9uX2dyYWRlc195ZWFyICU+JSBnYXRoZXIoa2V5LCB2YWx1ZSwgLUJPUk8sLWdyYWRlLC15ZWFyKQpSZWxGcmVxPC1mdW5jdGlvbihtKXsKICAgKChtICkvc3VtKG0pKQogfQppbnNwZWN0aW9uX2dyYWRlc195ZWFyPC1pbnNwZWN0aW9uX2dyYWRlc195ZWFyICU+JSBncm91cF9ieShCT1JPLGdyYWRlLHllYXIpICU+JQogICAgbXV0YXRlKHBlcmNlbnRhZ2UgPSAodmFsdWUvc3VtKHZhbHVlKSkqMTAwKQpnZ3Bsb3QoaW5zcGVjdGlvbl9ncmFkZXNfeWVhciwgYWVzKEJPUk8sIHBlcmNlbnRhZ2UgLGZpbGw9IGtleSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICtmYWNldF93cmFwKH55ZWFyLG5yb3c9MixuY29sPTMpKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iR3JhZGUiKSkrCiAgZ2d0aXRsZSgiJUdyYWRlIGRpc3RyaWJ1dGlvbiBieSBCT1JPVUdIIikKCmBgYAoKQWRkaW5nIHllYXIgdG8gdGhpcyBhbmFseXNpcyBzaG93ZWQgdXMgYW4gaW5jcmVhc2UgaW4gdGhlIHByb3BvcnRpYW4gb2YgQXMgaW4gU2F0YXRlbiBJc2xhbmQgaW4gMjAxNSBidXQgc2VlbWVkIGNvbnNpc3RlbnQgdGhyb3VnaG91dCB0aGUgcmVzdCBvZiB0aGUgcGxvdC4KCgpXZSBkZWNpZGVkIHRoYXQgYm9yb3VnaCBtYXkgYmUgdG9vIGdlbmVyYWwgYW5kIHRodXMgbG9va2VkIGF0IGdyYWRlIGRpc3RyaWJ1dGlvbiBieSB6aXAgY29kZSBhY3Jvc3MgdGhlIHZhcmlvdXMgeWVhcnMuIFdlIHVzZWQgYSBoZWF0IG1hcCB0byBkbyBzby4KYGBge3IsZmlnLmhlaWdodD0xMH0KbGlicmFyeSh2aXJpZGlzKQpub25ZZWFyRGF0YUZvckhlYXRNYXA8LVZpb2xhdGlvbnNEYXRhICU+JSBzZWxlY3QoR1JBREUsWklQQ09ERSkKI25vblllYXJEYXRhRm9ySGVhdE1hcDwtbm9uWWVhckRhdGFGb3JIZWF0TWFwJT4lbXV0YXRlKGdyYWRlWWVhcj1hcy5udW1lcmljKGZvcm1hdChHUkFERS5EQVRFICwgJyVZJykpKQpub25ZZWFyRGF0YUZvckhlYXRNYXAgPC0gbm9uWWVhckRhdGFGb3JIZWF0TWFwICU+JSBncm91cF9ieShHUkFERSxaSVBDT0RFKSAlPiUgdGFsbHkgClJlbEZyZXE8LWZ1bmN0aW9uKG0pewogICAoKG0gKS9zdW0obSkpCn0KCm5vblllYXJEYXRhRm9ySGVhdE1hcDwtICBub25ZZWFyRGF0YUZvckhlYXRNYXAgJT4lIGdyb3VwX2J5KFpJUENPREUpJT4lCiAgICBtdXRhdGUocGVyY2VudGFnZSA9IChuL3N1bShuKSkqMTAwKQoKZ2dwbG90KG5vblllYXJEYXRhRm9ySGVhdE1hcCwgYWVzKEdSQURFLCAKICAgICAgICAgICAgICAgICAgICAgICAgWklQQ09ERSwgZmlsbCA9IHBlcmNlbnRhZ2UpKSArCiAgZ2VvbV90aWxlKCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcygpICsKICAjZmFjZXRfd3JhcCh+Qk9STykKICBnZ3RpdGxlKCJQZXJjZW50YWdlIGdyYWRlIGRpc3RyaWJ1dGlvbiBieSB6aXBjb2RlIGFjcm9zcyB5ZWFyc1xuICIpKwogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0tNDUsIGhqdXN0PS4xKSwKICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKGI9LjUpLCBzaXplID0gMTUpKSt5bGFiKCJHcmFkZSIpCgoKYGBgCkRJU0NVU1MgV0lUSCBMQUtTSFlBCgpUaGUgbmV4dCBwYXJ0IG9mIG91ciBhbmFseXNpcyB3YXMgdG8gbG9vayBhdCB0aGUgYXZlcmFnZSBzY29yZXMgb2YgZWFjaCBjdWlzaW5lLiBXaXRoIHRoaXMgaW5mb3JtYXRpb24sIHdlIGNhbiBoZWxwIGNvbnN1bWVycyBzZWUgd2hhdCBraW5kcyBvZiBlc3RhYmxpc2htZW50cyBoYXZlIHRoZSBiZXN0IGFuZCB3b3JzdCBzY29yZXMgb24gYXZlcmFnZS4gVGhpcyBpbiB0ZXJtIGNhbiBoZWxwIGEgY29uc3VtZXIgY2hvb3NlIGEgdHlwZSBvZiBjdWlzaW5lIHdoZW4gdGhleSBhcmUgaHVuZ3J5LgoKYGBge3IsZmlnLmhlaWdodD0xMn0KYXZlcmFnZV9zY29yZXMgPC0gVmlvbGF0aW9uc0RhdGEgJT4lIHNlbGVjdCgtQ0FNSVMsIC1EQkEsIC1CVUlMRElORywgLVNUUkVFVCwgLVpJUENPREUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1QSE9ORSwgLUlOU1BFQ1RJT04uREFURSwgLUFDVElPTiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVZJT0xBVElPTi5DT0RFLCAtVklPTEFUSU9OLkRFU0NSSVBUSU9OLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtQ1JJVElDQUwuRkxBRywgLUdSQURFLCAtR1JBREUuREFURSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVJFQ09SRC5EQVRFLCAtSU5TUEVDVElPTi5UWVBFKQoKYXZlcmFnZV9jdWlzaW5lIDwtIGF2ZXJhZ2Vfc2NvcmVzICU+JSAgZ3JvdXBfYnkoQ1VJU0lORS5ERVNDUklQVElPTikgJT4lIG5hLm9taXQgJT4lCiAgc3VtbWFyaXplKGF2ZXJhZ2Vfc2NvcmUgPSBtZWFuKFNDT1JFKSkKYXZlcmFnZV9jdWlzaW5lIDwtIGFycmFuZ2UoYXZlcmFnZV9jdWlzaW5lLCAtYXZlcmFnZV9zY29yZSkKCmdncGxvdChhdmVyYWdlX2N1aXNpbmUsIGFlcyhyZW9yZGVyKHggPSBDVUlTSU5FLkRFU0NSSVBUSU9OLCAtLWF2ZXJhZ2Vfc2NvcmUpLCBhdmVyYWdlX3Njb3JlLCBmaWxsID0gQ1VJU0lORS5ERVNDUklQVElPTikpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIGNvb3JkX2ZsaXAoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykrZ2d0aXRsZSgiQXZlcmFnZSBzY29yZSBhY3Jvc3MgY3Vpc2luZXMiKSt4bGFiKCJDdWlzaW5lIGRlc2NyaXB0aW9uIikreWxhYigiYXZlcmFnZSBzY29yZSIpK2dlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjJmIiwgYXZlcmFnZV9zY29yZSkpLHBvc2l0aW9uPXBvc2l0aW9uX3N0YWNrKHZqdXN0PTAuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41LCBzaXplID0gMykKYGBgCgpgYGB7cn0Kd29yc3QxMCA8LSBhdmVyYWdlX2N1aXNpbmVbMToxMCxdCgpnZ3Bsb3Qod29yc3QxMCwgYWVzKHJlb3JkZXIoeCA9IENVSVNJTkUuREVTQ1JJUFRJT04sIC0tYXZlcmFnZV9zY29yZSksIGF2ZXJhZ2Vfc2NvcmUsIGZpbGwgPSBDVUlTSU5FLkRFU0NSSVBUSU9OKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgY29vcmRfZmxpcCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKStnZ3RpdGxlKCJBdmVyYWdlIHNjb3JlIG9mIFdvcnN0IDEwIGN1aXNpbmVzIikreGxhYigiQ3Vpc2luZSBkZXNjcmlwdGlvbiIpK3lsYWIoImF2ZXJhZ2Ugc2NvcmUiKStnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4yZiIsIGF2ZXJhZ2Vfc2NvcmUpKSxwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpKQpgYGAKCmBgYHtyfQpuIDwtIG5yb3coYXZlcmFnZV9jdWlzaW5lKQpiZXN0MTAgPC0gYXZlcmFnZV9jdWlzaW5lWyhuLSAxMCk6bixdCmJlc3QxMCA8LSBiZXN0MTBbb3JkZXIoYmVzdDEwJGF2ZXJhZ2Vfc2NvcmUpLF0KCmdncGxvdChiZXN0MTAsIGFlcyhyZW9yZGVyKHggPSBDVUlTSU5FLkRFU0NSSVBUSU9OLCAtYXZlcmFnZV9zY29yZSksIGF2ZXJhZ2Vfc2NvcmUsIGZpbGwgPSBDVUlTSU5FLkRFU0NSSVBUSU9OKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgY29vcmRfZmxpcCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKStnZ3RpdGxlKCJBdmVyYWdlIHNjb3JlIG9mIHRvcCAxMCBjdWlzaW5lcyIpK3hsYWIoIkN1aXNpbmUgZGVzY3JpcHRpb24iKSt5bGFiKCJhdmVyYWdlIHNjb3JlIikrZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMmYiLCBhdmVyYWdlX3Njb3JlKSkscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLCNwb3NpdGlvbj1wb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZqdXN0ID0gMC41LCBzaXplID0gNCkKYGBgCgpJdCBpcyBpbnRlcmVzdGluZyB0aGF0IHRoZSBteXN0ZXJpb3VzICJub3QgYXBwbGljYWJsZSIiIGN1aXNpbmUgaXMgb25lIG9mIHRoZSBiZXN0IQoKSW4gdGhpcyBuZXh0IHBsb3QsIHdlIHdhbnRlZCB0byBzZWUgaWYgdGhlIGF2ZXJhZ2Ugc2NvcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlZCBieSBCb3JvdWdoCmBgYHtyfQphdmVyYWdlX2Jvcm91Z2ggPC0gYXZlcmFnZV9zY29yZXMgJT4lICBncm91cF9ieShCT1JPKSAlPiUgbmEub21pdCAlPiUKICBzdW1tYXJpemUoYXZlcmFnZV9zY29yZSA9IG1lYW4oU0NPUkUpKQoKZ2dwbG90KGF2ZXJhZ2VfYm9yb3VnaCwgYWVzKHJlb3JkZXIoeCA9IEJPUk8sIC0tYXZlcmFnZV9zY29yZSksIGF2ZXJhZ2Vfc2NvcmUsIGZpbGwgPSBCT1JPKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKStnZ3RpdGxlKCJBdmVyYWdlIHNjb3JlIGFjcm9zcyBib3JvdWdocyIpK3hsYWIoIkJvcm91Z2giKSt5bGFiKCJhdmVyYWdlIHNjb3JlIikrZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuMmYiLCBhdmVyYWdlX3Njb3JlKSkscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIHNpemUgPSAzKQpgYGAKCkFsdGhvdWdoIHdlIHNlZSBzbGlnaHQgZGlmZmVyZW5jZSwgdGhlIGJvcm91Z2hzIGhhdmUgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBhdmVyYWdlIHNjb3JlLiBTdGF0ZW4gSXNsYW5kIG1heSBiZSB0aGUgb25lIGV4Y2VwdGlvbi4gV2UgYWxzbyBmYWNldGVkIGJ5IHllYXIgdG8gc2VlIGlmIHRoYXQgbWFkZSBhIGRpZmZlcmVuY2UsIGJ1dCB0aGUgcmVzdWx0cyB3ZXJlIGVzc2VudGlhbGx5IHRoZSBzYW1lIGFzIGluIHRoZSBwbG90IGFib3ZlLgoKU28gZmFyIHdlIGxvb2tlZCBhdCBncmFkZXMgYW5kIHNjb3JlcyBidXQgbm90IHRoZSBhY3R1YWwgdmlvbGF0aW9ucy4gV2hhcmUgYXJlIHRoZSB0b3AgdmlvbGF0aW9ucyByZXN0YXVyYW50cyB1c3VhbGx5IGZhY2U/CmBgYHtyLGZpZy53aWR0aD0xMn0KCnZpb2xhdGlvbnM8LSBWaW9sYXRpb25zRGF0YSAlPiUgc2VsZWN0KC1DQU1JUywgLURCQSwgLUJVSUxESU5HLCAtU1RSRUVULCAtWklQQ09ERSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVBIT05FLCAtSU5TUEVDVElPTi5EQVRFLCAtQUNUSU9OLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtVklPTEFUSU9OLkNPREUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1DUklUSUNBTC5GTEFHLCAtR1JBREUsIC1HUkFERS5EQVRFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUkVDT1JELkRBVEUsIC1JTlNQRUNUSU9OLlRZUEUpCgp2cyA8LSB2aW9sYXRpb25zICU+JSAgZ3JvdXBfYnkoVklPTEFUSU9OLkRFU0NSSVBUSU9OKSAlPiUgbmEub21pdCAlPiUKICBzdW1tYXJpemUoY291bnQgPSBuKCkpCnZzIDwtIGFycmFuZ2UodnMsIC1jb3VudCkKCnRvcHZpb2xhdGlvbnMgPC0gdnNbMToxMCxdCgpsaWJyYXJ5KHN0cmluZ3IpCnRvcHZpb2xhdGlvbnMkdmlvbCA9IHN0cl93cmFwKHRvcHZpb2xhdGlvbnMkVklPTEFUSU9OLkRFU0NSSVBUSU9OLCB3aWR0aCA9IDE1KQoKZ2dwbG90KHRvcHZpb2xhdGlvbnMsIGFlcyhyZW9yZGVyKHZpb2wsIC1jb3VudCksIGNvdW50LGZpbGw9dmlvbCkpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykrZ2d0aXRsZSgiVG9wIDEwIHZpb2xhdGlvbnMiKSt4bGFiKCJWaW9sYXRpb24gZGVzY3JpcHRpb24iKSt5bGFiKCJjb3VudCIpK2dlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjBmIiwgY291bnQpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIHNpemUgPSA3KQpgYGAKCldlIGFsc28gaHlwb3RoZXNpemVkIHRoYXQgdGhlIHR5cGUgb2YgaW5zcGVjdGlvbnMgd291bGQgZ2VuZXJhbGx5IHZhcnkgYnkgQm9yb3VnaCBidXQgZXZlbiB0aGlzIHdhcyBtb3N0bHkgY29uc2lzdGVudC4KYGBge3IsZmlnLmhlaWdodD02fQogbGlicmFyeSh2aXJpZGlzKQptb3M8LSBWaW9sYXRpb25zRGF0YSAlPiUgc2VsZWN0KC1DQU1JUywgLURCQSwgLUJVSUxESU5HLCAtU1RSRUVULCAtWklQQ09ERSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUEhPTkUsIC1BQ1RJT04sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLVZJT0xBVElPTi5DT0RFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1DUklUSUNBTC5GTEFHLCAtR1JBREUsIC1HUkFERS5EQVRFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1SRUNPUkQuREFURSwgLVNDT1JFLCAtQ1VJU0lORS5ERVNDUklQVElPTiwgLVZJT0xBVElPTi5ERVNDUklQVElPTikKbW9zJHllYXIgPC0gZmFjdG9yKGFzLm51bWVyaWMoIGZvcm1hdChtb3MkSU5TUEVDVElPTi5EQVRFICwgJyVZJykpKQptb3MgPC0gbW9zICU+JSBzZWxlY3QoLUlOU1BFQ1RJT04uREFURSkKCmF2ZXJhZ2VfbW9zIDwtIG1vcyAlPiUgZ3JvdXBfYnkoQk9STywgeWVhciwgSU5TUEVDVElPTi5UWVBFKSAlPiUgdGFsbHkgJT4lCiBncm91cF9ieShCT1JPLCB5ZWFyKSAgJT4lIAogbXV0YXRlKHAgPSBuIC8gc3VtKG4pKQojYXZlcmFnZV9jdWlzaW5lIDwtIGFycmFuZ2UoYXZlcmFnZV9jdWlzaW5lLCAtYXZlcmFnZV9zY29yZSkKCmxpYnJhcnkodmNkKQphdmVyYWdlX21vcyA8LSBhdmVyYWdlX21vcyAlPiUgc2VsZWN0KC1uKQoKCmdncGxvdChhdmVyYWdlX21vcywgYWVzKEJPUk8sIAogICAgICAgICAgICAgICAgICAgICAgIElOU1BFQ1RJT04uVFlQRSwgZmlsbCA9IHApKSArCiBnZW9tX3RpbGUoKSArCiBzY2FsZV9maWxsX3ZpcmlkaXMoKSArCiAjZmFjZXRfd3JhcCh+Qk9STykKIGdndGl0bGUoIkFWRVJBR0UgVklPTEFUSU9OIFNDT1JFIEJZIFpJUCBDT0RFXG4gIikrCiB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9LTQ1LCBoanVzdD0uMSksCiAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKGI9LjUpLCBzaXplID0gMTUpKStndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9InBlcmNlbnRhZ2UiKSkKCmBgYApPbmUgdGhpbmcgd2Ugbm90aWNlZCB3YXMgdGhhdCBTdGF0ZW4gaXNsYW5kIGhhcyB0aGUgbW9zdCBDeWNsZSBJbnNwZWN0aW9uL0luaXRpYWwgSW5zcGVjdGlvbi4gTWF5YmUgdGhleSB3ZXJlIG9wZW5pbmcgdXAgbWFueSBuZXcgcmVzdHVhcmFudHMuCgoKT25lIGZhY3RvciBvZiBvdXIgZGF0YSBzZXQgd2UgaGF2ZW4ndCBkaXNjdXNzZWQgdG9vIG11Y2ggaXMgdGhlIENyaXRpY2FsIGZsYWcsIHNheWluZyB3aGV0aGVyIG9yIG5vdCBhIHZpb2xhdGlvbiBpcyBjcml0aWNhbC4gT25jZSBhZ2Fpbiwgd2UgYXNzdW1lZCB0aGF0IG92ZXIgdGhlIHllYXJzIGFuZCBhY3Jvc3MgZGlmZmVyZW50IGJvcm91Z2hzLCB0aGVyZSB3b3VsZCBiZSBzb21lIHBhdHRlcm4gaW4gY3JpdGljYWwgdmlvbGF0aW9ucy4gQXMgY2FuIGJlIGluZmVycmVkIGZyb20gdGhlIGZvbGxvd2luZyBwbG90LCB0aGVyZSBpcyBub3QuCmBgYHtyLGZpZy53aWR0aD01fQpDcml0aWNhbGl0eURhdGE8LSBWaW9sYXRpb25zRGF0YSAlPiUgc2VsZWN0KEJPUk8sSU5TUEVDVElPTi5EQVRFLENSSVRJQ0FMLkZMQUcpICU+JSBuYS5vbWl0KCkKQ3JpdGljYWxpdHlEYXRhJHllYXI8LWZhY3Rvcihhcy5udW1lcmljKCBmb3JtYXQoQ3JpdGljYWxpdHlEYXRhJElOU1BFQ1RJT04uREFURSAsICclWScpKSkKI0NyaXRpY2FsaXR5RGF0YSA8LSBDcml0aWNhbGl0eURhdGEgJT4lc2VsZWN0KC1JTlNQRUNUSU9OLkRBVEUpCgpDcml0aWNhbGl0eURhdGEgPC0gQ3JpdGljYWxpdHlEYXRhICU+JXNlbGVjdCgtSU5TUEVDVElPTi5EQVRFKSAlPiVnYXRoZXIoa2V5LCB2YWx1ZSwgLUJPUk8sIC15ZWFyKSAlPiUgZ3JvdXBfYnkoQk9STywgeWVhcixrZXksdmFsdWUpICU+JXRhbGx5ICU+JSBzcHJlYWQodmFsdWUsIG4sIGZpbGwgPSAwKQpuYW1lcyhDcml0aWNhbGl0eURhdGEpWzVdIDwtICJOb3RBcHBsaWNhYmxlIgpuYW1lcyhDcml0aWNhbGl0eURhdGEpWzZdIDwtICJOb25Dcml0aWNhbCIKCkNyaXRpY2FsaXR5RGF0YVBlcmNlbnRhZ2U8LUNyaXRpY2FsaXR5RGF0YSAlPiUgc3VtbWFyaXplKGNyaXRpY2FsUGVyY2VudCA9IENyaXRpY2FsLyhDcml0aWNhbCtOb3RBcHBsaWNhYmxlK05vbkNyaXRpY2FsKSwgTm90QXBwbGljYWJsZVBlcmNlbnQgPSBOb3RBcHBsaWNhYmxlLyhDcml0aWNhbCtOb3RBcHBsaWNhYmxlK05vbkNyaXRpY2FsKSwgTm9uQ3JpdGljYWxQZXJjZW50ID0gTm9uQ3JpdGljYWwvKENyaXRpY2FsK05vdEFwcGxpY2FibGUrTm9uQ3JpdGljYWwpKSMKCm5hbWVzKENyaXRpY2FsaXR5RGF0YVBlcmNlbnRhZ2UpWzRdIDwtICJDcml0aWNhbCIKbmFtZXMoQ3JpdGljYWxpdHlEYXRhUGVyY2VudGFnZSlbNV0gPC0gIk5vdEFwcGxpY2FibGUiCm5hbWVzKENyaXRpY2FsaXR5RGF0YVBlcmNlbnRhZ2UpWzZdIDwtICJOb25Dcml0aWNhbCIKQ3JpdGljYWxpdHlEYXRhUGVyY2VudGFnZTwtQ3JpdGljYWxpdHlEYXRhUGVyY2VudGFnZSU+JSBnYXRoZXIoa2V5MSwgdmFsdWUsIC1CT1JPLCAteWVhciwta2V5KQoKCmdncGxvdChDcml0aWNhbGl0eURhdGFQZXJjZW50YWdlLCBhZXMoQk9STywgdmFsdWUsIGZpbGwgPSBrZXkxKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSsgeGxhYigiQk9ST1VHSCIpK3lsYWIoIkNSSVRJQ0FMSVRZIFBFUkNFTlRBR0VTIikgK3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpKyBnZ3RpdGxlKCJHcm91cGVkIGJhcmNoYXJ0IG9mIGdyYWRlIGZyZXF1ZW5jeSBpbiBlYWNoIGN1aXNpbmUgY2F0ZWdvcnkgXG4gd2hlbiB0aGVyZSB3YXMgYSB2aW9sYXRpb24gcmVwb3J0ZWQiKStmYWNldF93cmFwKH55ZWFyLG5yb3c9MixuY29sPTMpK2d1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iQ3JpdGljYWxpdHkgXG4gY2F0ZWdvcnkiKSkKCmBgYApXZSBhdHRlbXB0ZWQgdG8gZnVydGhlciBhbmFseXplIHRoZSBjcml0aWNhbCBmbGFnIGJ1dCB3ZSBsZWFybmVkIHRoYXQgdGhlIGNyaXRpY2FsIGZsYWcgZGlkIG5vdCBoYXZlIHRvbyBtdWNoIGltcGFjdCBvbiBncmFkZSBvciBzY29yZXMuIFRoZXJlIHdhcyBhIGxvdCBvZiBkYXRhIHdpdGggYSBncmFkZSBvZiBBIGJ1dCBhIENyaXRpY2FsIGZsYWcgd2hpbGUgb3RoZXJzIHdpdGggYSB2ZXJ5IGhpZ2ggc2NvcmUgYnkgbm8gY3JpdGljYWwgZmxhZy4KCkZvciB0aGUgcmVtYWluZGVyIG9mIHRoZSBhbmFseXNpcywgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggVGFibGVhdS4gV2UgY2hvc2UgVGFibGVhdSBiZWNhdXNlIHdlIHdhbnRlZCB0byBwbG90IGVsZWdlbnQgbWFwcy4gVGFibGVhdSBwcm92aWRlcyBhIHNpbXBsZXIgYW5kIG1vcmUgZWxlZ2VudCB3YXkgdG8gZG8gc28gdGhhbiBpbiBSLgoKVGhlIGNvZGUgY2h1bmtzIHdpbGwgZ2VuZXJhdGUgdGhlIGRhdGEgdGhhdCB3ZSB1cGxvYWQgdG8gVGFibGVhdS4gSW4gdGhlIGZvbGxvd2luZyBhbmFseXNpcywgd2UgZXhwbG9yZSB0aGUgcHJvcG9ydGlvbiBvZiBBIGdyYWRlcyBiYXNlZCBvbiBuZWlnaGJvcmhvb2Qgb3ZlciB0aGUgeWVhcnMuCmBgYHtyLGZpZy5oZWlnaHQ9MTV9CiMjIyMjR0VORVJBVEUgR1JBREUgUFJPUE9FUlRJT04gREFUQVNFVCBCWSBZRUFSIEZPUiBUQUJMRUFVIFBMT1RUSU5HCnppcGRhdGE8LSBWaW9sYXRpb25zRGF0YSAlPiUgc2VsZWN0KC1DQU1JUywgLURCQSwgLUJVSUxESU5HLCAtU1RSRUVULCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUEhPTkUsIC1BQ1RJT04sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1WSU9MQVRJT04uQ09ERSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLUNSSVRJQ0FMLkZMQUcsIC1TQ09SRSwgLUdSQURFLkRBVEUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1SRUNPUkQuREFURSwgLUlOU1BFQ1RJT04uVFlQRSwgLVZJT0xBVElPTi5ERVNDUklQVElPTiwgLUNVSVNJTkUuREVTQ1JJUFRJT04sIC1CT1JPKSAlPiUgbmEub21pdCgpCnppcGRhdGEkeWVhcjwtZmFjdG9yKGFzLm51bWVyaWMoIGZvcm1hdCh6aXBkYXRhJElOU1BFQ1RJT04uREFURSAsICclWScpKSkKemlwZGF0YUdyYWRlUGVyY2VudGFnZSA8LSB6aXBkYXRhICU+JXNlbGVjdCgtSU5TUEVDVElPTi5EQVRFKSAlPiVnYXRoZXIoa2V5LCB2YWx1ZSwgLVpJUENPREUsIC15ZWFyKSAlPiUgZ3JvdXBfYnkoWklQQ09ERSwgeWVhciwga2V5LCB2YWx1ZSkgJT4lCiAgdGFsbHkgJT4lIHNwcmVhZCh2YWx1ZSwgbiwgZmlsbCA9IDApICU+JSBzdW1tYXJpemUoYXByb3AgPSBBLyhBK0IrQyksIGJwcm9wID0gQi8oQStCK0MpLCBjcHJvcCA9IEMvKEErQitDKSApCgojIGdncGxvdCh6aXBkYXRhR3JhZGVQZXJjZW50YWdlLCBhZXMocmVvcmRlcih4ID0gWklQQ09ERSwgLS1hcHJvcCksIGFwcm9wKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsgCiMgICBjb29yZF9mbGlwKCkgKyBmYWNldF93cmFwKH55ZWFyKQojICAgdGhlbWUoIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMykpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBcHJvcDEucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0Fwcm9wMi5wbmcnKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQXByb3AzLnBuZycpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBcHJvcDQucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0Fwcm9wNS5wbmcnKQpgYGAKCk92ZXIgdGhlIHllYXJzLCB0aGUgcHJvcG9ydGlvbiBvZiBBJ3MgaGFzIGluY3JlYXNlZCwgYXMgcmVwcmVzZW50ZWQgYnkgdGhlIGRhcmtlciByZWQgc2hhZGluZyBvZiB0aGUgbWFwLiBJdCBpcyB0b28gZWFybHkgdG8gc3R1ZHkgMjAxNyBpbiBkZXB0aCBkdWUgdG8gdGhlIGxpbWl0ZWQgbnVtYmVyIG9mIGluc3BlY3Rpb25zIHNvIGZhci4KCkxldCdzIGxvb2sgYXQgaG93IHRoZSBwcm9wb3J0aW9uIG9mIEIncyBjaGFuZ2UuCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0Jwcm9wMS5wbmcnKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQnByb3AyLnBuZycpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdCcHJvcDMucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0Jwcm9wNC5wbmcnKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQnByb3A1LnBuZycpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IG92ZXIgdGhlIHllYXJzLCB0aGVyZSBzZWVtIHRvIGJlIGxlc3MgQidzIHdoaWNoIG1ha2VzIHNlbnNlIHNpbmNlIG92ZXIgdGhlIHllYXJzIHdlIGhhdmUgbW9yZSBncmFkZXMgb2YgQS4KCkxldCdzIGxvb2sgYXQgdGhlIGF2ZXJhZ2Ugc2NvcmVzIGJ5IG5laWdoYm9yaG9vZC4gV2UgaGF2ZSBhbHJlYWR5IGRvbmUgc28gYnkgY3Vpc2luZS4KCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQXZnVmlvbFNjb3JlQnlaaXAyMDEzLnBuZycpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBdmdWaW9sU2NvcmVCeVppcDIwMTQucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1Zpb2xTY29yZUJ5WmlwMjAxNS5wbmcnKQpgYGAKCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnQXZnVmlvbFNjb3JlQnlaaXAyMDE2LnBuZycpCmBgYAoKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdBdmdWaW9sU2NvcmVCeVppcDIwMTcucG5nJykKYGBgCgpPdmVyIHRoZSB5ZWFycywgd2UgaGF2ZSBhIGxvd2VyIEF2ZXJhZ2UgU2NvcmUuIFRoZXNlIE5ZQyBpbnNwZWN0aW9ucyBtdXN0IGJlIHByb21wdGluZyByZXN0dXJhbnRzIHRvIGltcHJvdmUgdGhlaXIgZmFjaWxpdGllcyBhbmQgZm9sbG93IHJlZ3VsYXRpb25zIQoKV2UgZGVjaWRlZCB0byBzZWUgd2hpY2ggYXJlYXMgaGFkIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgbnVtYmVyIG9mIHJlaW5zcGVjdGlvbnMuIE1heWJlIHRoaXMgY2FuIGV4cGxhaW4gdGhlIGJldHRlciBzY29yZXMgYW5kIGdyYWRlcyBvdmVyIHRpbWUuCgpgYGB7cn0KIyMjIERBVEEgR0VORVJBVElPTgpsaWJyYXJ5KHN0cmluZ3IpCnJlaW5zcGVjdCA8LSBWaW9sYXRpb25zRGF0YSAlPiUgc2VsZWN0KC1DQU1JUywgLURCQSwgLUJVSUxESU5HLCAtU1RSRUVULCAtQ1VJU0lORS5ERVNDUklQVElPTiwgLUJPUk8sIC1TQ09SRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1QSE9ORSwgSU5TUEVDVElPTi5EQVRFLCAtQUNUSU9OLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1WSU9MQVRJT04uQ09ERSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtQ1JJVElDQUwuRkxBRywgLUdSQURFLCAtR1JBREUuREFURSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUkVDT1JELkRBVEUpCnJlaW5zcGVjdCR5ZWFyPC1mYWN0b3IoYXMubnVtZXJpYyggZm9ybWF0KHJlaW5zcGVjdCRJTlNQRUNUSU9OLkRBVEUgLCAnJVknKSkpCgojaW5zcGVjdGlvbl90eXBlcyA8LSBJTlNQRUNUSU9OLlRZUEUKcmVpbnNwZWN0IDwtIHJlaW5zcGVjdCAlPiVzZWxlY3QoLUlOU1BFQ1RJT04uREFURSkgICU+JSBuYS5vbWl0ICU+JSBmaWx0ZXIoc3RyX2RldGVjdChJTlNQRUNUSU9OLlRZUEUsICJSZS1pbnNwZWN0aW9uIikpJT4lZ3JvdXBfYnkoVklPTEFUSU9OLkRFU0NSSVBUSU9OLCB5ZWFyLCBaSVBDT0RFICkgICU+JSB0YWxseSMgc3VtbWFyaXNlKHJlID0gc3VtKHN0cl9jb3VudChyZWluc3BlY3QkSU5TUEVDVElPTi5UWVBFLCAiUmUtaW5zcGVjdGlvbiIpKSkKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1JlaW5zcGVjdDIwMTMucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1JlaW5zcGVjdDIwMTQucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1JlaW5zcGVjdDIwMTUucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1JlaW5zcGVjdDIwMTYucG5nJykKYGBgCgpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0F2Z1JlaW5zcGVjdDIwMTcucG5nJykKYGBgCgpJbnRlcmVzdGluZ2x5IGVub3VnaCwgdGhlIGFyZWFzIHdpdGggdGhlIG1vc3QgcmVpbnNwZWN0aW9ucyBzZWVtIHRvIGJlIHRoZSBvbmVzIGluIHdoaWNoIHRoZSBncmFkZXMgYW5kIHNjb3JlcyBpbXByb3ZlZC4gVGhlIHN5c3RlbSBvZiByZWluc3BlY3Rpb25zIG11c3QgYmUgd29ya2luZyEKCgpBcyBhIGxhc3QgYml0IG9mIGFuYWx5c2lzLCB3ZSBkZWNpZGVkIHRvIGhlbHAgc2V0dGxlIGEgaHVnZSBkaWxlbW5hIDogV2hlcmUgc2hvdWxkIHlvdSBnZXQgeW91ciBjb2ZmZWUsIFN0YXJidWNrcyBvciBEdW5raW5nIERvbnV0cz8KCmBgYHtyfQojI0RBVEEgR0VORVJBVElPTiBDT0RFCmxpYnJhcnkoc3RyaW5ncikKQ2hhaW5zc0RmPC1WaW9sYXRpb25zRGF0YSU+JSBtdXRhdGUoaXNTdGFyYnVja3M9c3RyX2RldGVjdChEQkEsIlNUQVJCVUNLIikpICU+JSBtdXRhdGUoaXNEdW5raW49c3RyX2RldGVjdChEQkEsIkRVTktJTiIpKQpDaGFpbnNzRGYkREJBW0NoYWluc3NEZiRpc1N0YXJidWNrcz09VFJVRV08LSJTVEFSQlVDS1MiCkNoYWluc3NEZiREQkFbQ2hhaW5zc0RmJGlzRHVua2luPT1UUlVFXTwtIkRVTktJTiIKQ2hhaW5zc0RmPC1DaGFpbnNzRGYlPiVmaWx0ZXIoREJBICVpbiUgYygiU1RBUkJVQ0tTIiwiRFVOS0lOIikpCkNoYWluc3NEZjwtQ2hhaW5zc0RmJT4lc2VsZWN0KERCQSxCT1JPLElOU1BFQ1RJT04uREFURSxaSVBDT0RFLFNDT1JFLFZJT0xBVElPTi5ERVNDUklQVElPTixHUkFERSkKQ2hhaW5zc0RmJHllYXI8LWZhY3Rvcihhcy5udW1lcmljKCBmb3JtYXQoQ2hhaW5zc0RmJElOU1BFQ1RJT04uREFURSAsICclWScpKSkKQ2hhaW5zc0RmPC1DaGFpbnNzRGYlPiVzZWxlY3QoLUlOU1BFQ1RJT04uREFURSkKYGBgCgpBcyB5b3UgY2FuIHNlZSwgRHVua2luZyBEb251dHMgaGFzIG1vcmUgY3JpdGljYWwgdmlvbGF0aW9ucyB0aGFuIFN0YXJidWNrcyBpbiBldmVyeSBCb3JvdWdoLiBOb3RpY2libGUgZGlmZmVyZW5jZXMgYXJlIGVzcGVjaWFsbHkgc2VlIGluIEJyb29rbHluIGFuZCBNYW5oYXR0YW4uCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygnRFMxLnBuZycpCmBgYAoKCkJvdGggZXN0YWJsaXNobWVudHMgc2hhcmVkIHRoZSBzYW1lIHRvcCB2aW9sYXRpb25zLiBMZXQncyBzZWUgd2hhdCBwZXJjZW50YWdlIG9mIHRoZXNlIHZpb2xhdGlvbnMgZWFjaCBoYXMuIApgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0RTMi5wbmcnKQpgYGAKT25jZSBhZ2FpbiwgU3RhcmJ1Y2tzIGlzIHRoZSB3aW5uZXIhCgpXaGF0IGFib3V0IHRoZSBhdmVyYWdlIHNjb3JlPwpgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0RTMy5wbmcnKQpgYGAKClJlZ2FyZGxlcyBvZiB0aGUgeWVhciwgdGhlIGF2ZXJhZ2Ugc2NvcmUgb2YgRHVua2luZyBEb251dHMgaGFzIGJlZW4gaGlnaGVyLiBUaGUgaGlnaGVyIHRoZSBzY29yZSwgdGhlIHdvcnNlLiBOb3RpY2libGUgZGlmZmVyZW5jZXMgYXJlIHNlZW4gaW4gMjAxNSBhbmQgdGhlIGZpcnN0IHRocmVlIG1vbnRocyBvZiAyMDE3LgoKTGV0J3Mgc2VlIGlmIHRoZSBCb3JvdWdoIHlvdXIgaW4gc2hvdWxkIGltcGFjdCB5b3VyIGNob2ljZS4KYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCdEUzQucG5nJykKYGBgCgpJIGd1ZXNzIHdoZW4geW91J3JlIGluIEJyb29rbHluLCBpdCBkb2Vzbid0IG1hdHRlciBidXQgZXZlcnl3aGVyZSBlbHNlLCBzdGljayB0aGUgU3RhcmJ1Y2tzLCBlc3BlY2lhbGx5IHlvdSBTdGF0ZW4gSXNsYW5kIGZvbGtzIQoKIyMgQ29uY2x1c2lvbgpNYW55IGxpbWl0YXRpb25zIGluY2x1ZGVkIHRoZSBtaXNzaW5nIGRhdGEgaXRzZWxmLiBXZSBsZWFybmVkIHRoYXQgc29tZXRpbWVzLCB3aGF0IG1heSBzZWVtIGxpa2UgYW4gaW50dWl0aXZlIGh5cG90aGVzaXMsIGlzIGFjdHVhbGx5IGNvbXBsZXRlbHkgZmFsc2UuIFRoZXJlIHdlcmUgbm90IGFzIG1hbnkgcGF0dGVybnMgYXMgd2UgYW50aWNpcGF0ZWQgaW4gdGVybXMgb2YgQm9yb3VnaC4gVGhlIG9ubHkgdGhpbmcgd2UgZGlkIHNlZSBpcyB0aGF0IG92ZXIgdGltZSwgdGhlIHNjb3JlIGFuZCBncmFkZSBoYXMgaW5jcmVhc2VkLCBzaG93aW5nIHVzIHRoYXQgdGhlIGluc3BlY3Rpb25zIGFyZSB3b3JraW5nIGFuZCB0aGF0IHJlc3R1YXJhbnRzIGFyZSBpbXByb3ZpbmcgdGhlaXIgZmNpbGl0aWVzIHRvIHJlY2VpZXZlIGEgYmV0dGVyIHNjb3JlLiBBY2NvcmRpbmcgdG8gdGhlIHdlYnNpdGUgd2UgcmVjZXZlZCB0aGUgZGF0YSBmcm9tZSwgdGhyZWUgZmllbGRzIGFyZSBzb29uIHRvIGJlIGFkZGVkIGFzIGRhdGEgbG9naWMgYmVjb21lcyBhdmFpbGFibGUgdG8gcG9wdWxhdGUgdGhlbSBhY2N1cmF0ZWx5LiBUaG9zZSBmaWVsZHMgYXJlIFZJT0xBVElPTiBQT0lOVFMgKHRoZSBwb2ludHMgYXNzaWduZWQgdG8gYSB2aW9sYXRpb24gYmVmb3JlIG9yIGFmdGVyIGFkanVkaWNhdGlvbiwgZGVwZW5kaW5nIG9uIHdoZXRoZXIgYWRqdWRpY2F0aW9uIGhhcyBvY2N1cnJlZCksIEZJTkVTIFRPVEFMICh0aGUgZmluZSBhbW91bnQgYWZ0ZXIgYWRqdWRpY2F0aW9uKSwgYW5kIERFQ0lTSU9OIERBVEUgKGFkanVkaWNhdGlvbiBkYXRlIOKAkyBvciBkYXRlIGEgZ3JhZGUgYmVjb21lcyBmaW5hbCkuIFdpdGggdGhpcyBpbmZvcm1hdGlvbiwgd2UgY2FuIGRvIG1vcmUgYW5hbHlzaXMgdG8gc2VlIHRoZSBmaW5lcyBvZiB2YXJpb3VzIHZpb2xhdGlvbnMgYW5kIGxlYXJuIGhvdyBlYWNoIHZpb2xhdGlvbiBhY3R1YWxseSBhZmZlY3RzIHRoZSBzY29yZS4gV2l0aCB0aGF0LCByZXN0YXVyYW50cyBjYW4gZm9jdXMgb24gdGhlIG1haW4gdmlvbGF0aW9ucyB0byBpbXByb3ZlIHRoZWlyIHNjb3JlcyBpbiB0aGUgbmV4dCBpbnNwZWN0aW9uLgoKCgoKCg==